@ignitionai/storage
Single responsibility: persist and fetch trained agent weights. The first implementation targets HuggingFace Hub; the ModelStorageProvider interface is the extension point for other backends (S3, IPFS, local filesystem, whatever you need).
Source: packages/storage/src/ on GitHub.
Public API surface
| Export | Kind | Purpose |
|---|---|---|
ModelStorageProvider | interface | The contract any storage backend must implement. |
ModelInfo | type | Metadata for a stored model (modelId, uri, createdAt, metadata). |
HuggingFaceProvider | class | Cloud persistence via HuggingFace Hub. |
IndexedDBProvider | class | Browser persistence for large models (recommended). |
LocalStorageProvider | class | Browser persistence for small models & metadata. |
DownloadProvider | class | Export model as downloadable file (write-only). |
HFStorageConfig | type | Config for HuggingFaceProvider (token, repoId). |
hfStorageConfigSchema | Zod schema | Validation for HFStorageConfig. |
parseHFConfig | function | Parses and validates the HF config from environment variables. |
The ModelStorageProvider interface
This is the contract. Anything implementing it is a valid storage backend:
export interface ModelStorageProvider {
save(modelId: string, model: tf.LayersModel, metadata?: Record<string, unknown>): Promise<void>
load(modelId: string): Promise<tf.LayersModel>
list(): Promise<ModelInfo[]>
delete(modelId: string): Promise<void>
exists(modelId: string): Promise<boolean>
}Five methods. That’s the whole surface area. If you wanted to add, say, an S3 backend, you’d implement those five methods and publish it as @ignitionai/storage-s3. The rest of the framework doesn’t need to change.
Browser-local providers
For development and small models, you don’t need a cloud account. Three browser-native providers ship out of the box:
| Provider | Best for | Size limit | Load support |
|---|---|---|---|
IndexedDBProvider | Large TF.js models | ~50-100 MB (browser quota) | ✅ Yes |
LocalStorageProvider | Small models, configs, metadata | ~5 MB | ✅ Yes |
DownloadProvider | Exporting for external use | No limit (user’s disk) | ❌ No |
IndexedDB — the recommended default
import { IgnitionEnvTFJS } from '@ignitionai/backend-tfjs'
import { CartPoleEnv } from '@ignitionai/environments'
import { IndexedDBProvider } from '@ignitionai/storage'
const env = new IgnitionEnvTFJS(new CartPoleEnv())
env.train('dqn', { storageProvider: new IndexedDBProvider() })
// Later — save the current checkpoint
await env.save('cartpole-dqn-v1', { reward: 195.4 })
// Even later — load it back
await env.load('cartpole-dqn-v1')env.save() serializes the model weights and the agent’s internal state (epsilon, trainStepCounter, bestReward). env.load() restores both. No manual bookkeeping.
LocalStorage — for configs and Q-Tables
import { LocalStorageProvider } from '@ignitionai/storage'
const env = new IgnitionEnvTFJS(new CartPoleEnv())
env.train('qtable', { storageProvider: new LocalStorageProvider() })
await env.save('cartpole-qtable-v1')Q-Table agents serialize their entire table as JSON, so LocalStorageProvider is a perfect fit. For neural-network agents, prefer IndexedDBProvider — TF.js weights can easily exceed the 5 MB localStorage limit.
Download — export for external use
import { DownloadProvider } from '@ignitionai/storage'
const env = new IgnitionEnvTFJS(new CartPoleEnv())
env.train('dqn')
// Triggers a browser download of model.json + weights.bin
await env.save('my-model', { download: true })DownloadProvider is write-only. Use it when you want to hand off a trained model to someone else or archive it outside the browser.
HuggingFace Hub — cloud persistence
HuggingFaceProvider wraps the huggingface.js client and saves / loads TF.js models directly from a HF repo. HF Hub is a natural fit for RL weights because it already handles versioning, public/private visibility, and a web UI for browsing.
Setup
- Create a HuggingFace account and a new model repository (e.g.,
your-user/my-rl-agent). - Generate an access token at huggingface.co/settings/tokens with Write access to the repo.
- Set the token and repo as environment variables (or pass them inline):
export HF_TOKEN=hf_...
export HF_REPO_ID=your-user/my-rl-agentA complete save / load round-trip
import { IgnitionEnvTFJS } from '@ignitionai/backend-tfjs'
import { CartPoleEnv } from '@ignitionai/environments'
import { HuggingFaceProvider, parseHFConfig } from '@ignitionai/storage'
// 1. Train an agent
const cartpole = new CartPoleEnv()
const env = new IgnitionEnvTFJS(cartpole)
env.train('dqn', {
storageProvider: new HuggingFaceProvider(parseHFConfig(process.env))
})
// ... wait for convergence ...
env.stop()
// 2. Save the trained model (weights + agent state + metadata)
await env.save('cartpole-dqn-v1', {
episodesTrained: 500,
meanReward: 195.4,
})
// 3. Later — load the same model into a fresh agent
await env.load('cartpole-dqn-v1')
console.log('Model loaded, epsilon:', (env.agent as any).epsilon)Four lines to save. Two lines to load. The metadata object is stored alongside the weights and is returned by list().
Listing and deleting
import { HuggingFaceProvider, parseHFConfig } from '@ignitionai/storage'
const storage = new HuggingFaceProvider(parseHFConfig(process.env))
// See what's in the repo
const models = await storage.list()
for (const model of models) {
console.log(`${model.modelId} — created ${model.createdAt}`)
console.log(` metadata: ${JSON.stringify(model.metadata)}`)
}
// Check if a specific model exists
if (await storage.exists('cartpole-dqn-v1')) {
// Delete it
await storage.delete('cartpole-dqn-v1')
}Security note — tokens
The HF token has Write access to the repo, so treat it like a password:
- Never hard-code it into source files.
- In Node, read from
process.env.HF_TOKEN. - In the browser, do not use a write-enabled token at all — use a read-only token (or no token at all for public repos) for the load path, and do your writes from a backend.
parseHFConfigwill throw if the token is missing or malformed, so you’ll notice the misconfiguration immediately.
Using it alongside the ONNX path
The HF Hub flow is for TF.js-native weights (training + checkpointing). If you want to deploy a trained model, you can either:
- Save the TF.js weights via
@ignitionai/storage— fastest for iterating between train and test runs in JS. - Export to ONNX via
@ignitionai/backend-onnx— necessary for deploying to Unity, Unreal, Python, or mobile.
These aren’t mutually exclusive. A common workflow is: save TF.js checkpoints to HF during training (fast iteration), and export the final version to ONNX once you’re happy with it (deploy).
Adding your own storage backend
To add an S3 backend:
import type { ModelStorageProvider, ModelInfo } from '@ignitionai/storage'
import * as tf from '@tensorflow/tfjs'
export class S3Provider implements ModelStorageProvider {
constructor(private bucket: string, private region: string) {}
async save(modelId: string, model: tf.LayersModel, metadata?: Record<string, unknown>): Promise<void> {
// ... serialize model, upload to S3 ...
}
async load(modelId: string): Promise<tf.LayersModel> {
// ... download from S3, deserialize ...
}
async list(): Promise<ModelInfo[]> { /* ... */ return [] }
async delete(modelId: string): Promise<void> { /* ... */ }
async exists(modelId: string): Promise<boolean> { /* ... */ return false }
}Five methods, zero changes to any other package. That’s the shape of every good extension point in IgnitionAI.
Previous: ← @ignitionai/backend-onnx · Next: React Three Fiber →