Docs/AI & agentsv1.0.0-rc.1

AI Decorators

Overview

The AI decorator layer bridges framework-level decorator metadata and the runtime AI feature classes (StateAnalyzer, AIOptimizer, AIPredictor). @AIPowered is the primary entry point: it wraps the store constructor to create and wire all three AI components into the dispatch pipeline automatically. A set of helper functions then provides access to those instances from outside the store.

Public component AI decorators are experimental. The internal AIController and AIEndpoint decorators are not public root exports; see the Decorator Reference. This page focuses on the store AI system.

Key Concepts

  • @AIPowered wraps the store constructor; setup runs asynchronously after construction (in a setTimeout(0)) so it never blocks initialization.
  • Dispatch interception — @AIPowered replaces store.dispatch with an instrumented version that records transitions to the predictor, latencies to the optimizer, and runs analysis on every state change.
  • Helper functions — getAnalyzer, getOptimizer, getPredictor, analyzeStore, getAIInsights retrieve attached AI instances without accessing private properties.
  • enableAI — programmatic alternative to the decorator; useful for dynamic or conditional AI feature activation.

API Reference

AIPoweredOptions

interface AIPoweredOptions {
  analyzer?: StateAnalyzerConfig;      // config forwarded to StateAnalyzer
  optimizer?: OptimizerConfig;         // config forwarded to AIOptimizer
  predictor?: PredictorConfig;         // config forwarded to AIPredictor
  autoAnalyze?: boolean;               // run analysis on every dispatch; default true
  autoOptimize?: boolean;              // generate optimizer suggestions on every dispatch; default true
  logInsights?: boolean;               // print AI insights to console; default false
  onSuggestion?: (suggestion: OptimizationSuggestion) => void;
  onAnomaly?: (anomaly: Anomaly) => void;
}

@AIPowered

function AIPowered(options?: AIPoweredOptions): ClassDecorator

Applied to a Store subclass. Creates StateAnalyzer, AIOptimizer, and AIPredictor instances and attaches them as __analyzer__, __optimizer__, __predictor__ on the store instance.

Helper functions

// Retrieve AI instances attached by @AIPowered (or enableAI)
function getAnalyzer(store: any): StateAnalyzer | undefined
function getOptimizer(store: any): AIOptimizer | undefined
function getPredictor(store: any): AIPredictor | undefined

// Check whether AI is active on a store
function hasAI(store: any): boolean

// Run a full on-demand analysis
function analyzeStore(store: any): {
  score: number;
  issues: AnalysisIssue[];
  suggestions: OptimizationSuggestion[];
  recommendations: string[];
}

// Get a combined AI snapshot
function getAIInsights(store: any): {
  analysis?: AnalysisResult;
  suggestions?: OptimizationSuggestion[];
  anomalies?: Anomaly[];
}

// Programmatically attach AI features (no decorator required)
function enableAI(store: any, options?: AIPoweredOptions): void

Metadata helper

function getAIMetadata(target: any): { options: AIPoweredOptions } | undefined

Basic Usage

import 'reflect-metadata';
import {
  ReactiveStore, AIPowered, Store, Mutation, Action, Selector,
  getAnalyzer, getOptimizer, getPredictor, analyzeStore,
} from '@tagna/udiot';

interface TaskState {
  tasks: Task[];
  filter: 'all' | 'active' | 'done';
  loading: boolean;
}

@ReactiveStore({ name: 'TaskStore', trackHistory: true })
@AIPowered({
  autoAnalyze: true,
  autoOptimize: true,
  logInsights: process.env.NODE_ENV === 'development',
  onSuggestion: (s) => console.log(`[AI Suggestion] ${s.title}`),
  onAnomaly: (a) => console.error(`[AI Anomaly] ${a.severity}: ${a.description}`),
  analyzer: { complexityThreshold: 12 },
  optimizer: { latencyThreshold: 20 },
  predictor: { minConfidenceThreshold: 0.7, historySize: 150 },
})
class TaskStore extends Store<TaskState> {
  constructor() {
    super({ tasks: [], filter: 'all', loading: false });
  }

  @Mutation({ aiTracking: true })
  setFilter(filter: TaskState['filter']): void {
    this.dispatch({ type: 'SET_FILTER', payload: filter });
  }

  @Action({ retry: 2 })
  async fetchTasks(): Promise<void> {
    const tasks = await fetch('/api/tasks').then(r => r.json());
    this.dispatch({ type: 'SET_TASKS', payload: tasks });
  }

  @Selector()
  visibleTasks(): Task[] {
    const { tasks, filter } = this.getState();
    if (filter === 'all') return tasks;
    return tasks.filter(t => (filter === 'done') === t.done);
  }
}

Common Patterns

On-demand analysis

import { analyzeStore } from '@tagna/udiot';

const store = new TaskStore();

// After some user interaction
document.querySelector('#analyze-btn')?.addEventListener('click', () => {
  const report = analyzeStore(store);
  console.log(`State health score: ${report.score}/100`);
  report.issues.forEach(issue => console.warn(`${issue.severity}: ${issue.message}`));
  report.recommendations.forEach(r => console.info(`Recommendation: ${r}`));
});

Conditional AI activation

import { enableAI, Store } from '@tagna/udiot';

class AppStore extends Store<AppState> {
  constructor(state: AppState) { super(state); }
}

const store = new AppStore(initialState);

// Only enable AI in development
if (process.env.NODE_ENV === 'development') {
  enableAI(store, {
    autoAnalyze: true,
    logInsights: true,
    onAnomaly: (a) => console.error('[DEV] Anomaly:', a.description),
  });
}

Combining with @EnableDevTools and @Persist

import 'reflect-metadata';
import {
  ReactiveStore, AIPowered, EnableDevTools, Persist, Store, Mutation, Selector
} from '@tagna/udiot';

interface CartState { items: CartItem[]; coupon: string | null; }

@ReactiveStore({ name: 'CartStore' })
@AIPowered({ autoAnalyze: true, autoOptimize: true })
@EnableDevTools({ consoleLogger: true })
@Persist({ storage: 'localStorage', whitelist: ['items', 'coupon'] })
class CartStore extends Store<CartState> {
  constructor() { super({ items: [], coupon: null }); }

  @Mutation({ aiTracking: true })
  addItem(item: CartItem): void {
    this.dispatch({ type: 'ADD_ITEM', payload: item });
  }

  @Selector()
  total(): number {
    return this.getState().items.reduce((s, i) => s + i.price * i.qty, 0);
  }
}

Reacting to AI suggestions in production

import { ReactiveStore, AIPowered, Store } from '@tagna/udiot';
import type { OptimizationSuggestion, Anomaly } from '@tagna/udiot';
import { telemetryClient } from './telemetry';

@ReactiveStore({ name: 'MonitoredStore' })
@AIPowered({
  autoAnalyze: true,
  autoOptimize: true,
  onSuggestion: (s: OptimizationSuggestion) => {
    if (s.impact === 'high' && s.priority >= 8) {
      telemetryClient.track('ai.optimization.suggestion', {
        id: s.id, type: s.type, title: s.title,
      });
    }
  },
  onAnomaly: (a: Anomaly) => {
    telemetryClient.captureError(new Error(a.description), {
      severity: a.severity,
      recommendation: a.recommendation,
    });
  },
})
class MonitoredStore extends Store<AppState> {
  constructor(state: AppState) { super(state); }
}

Integration Notes

  • @AIPowered must be applied below @ReactiveStore in the decorator stack (decorators are applied bottom-to-top, so write @ReactiveStore above @AIPowered). If @Persist and @EnableDevTools are also applied, the order is @ReactiveStore → @AIPowered → @EnableDevTools → @Persist from top to bottom.
  • The AI setup (setupAIFeatures) runs inside setTimeout(fn, 0) — it executes after the store constructor returns. Do not call getAnalyzer or similar helpers synchronously in the constructor; wait for at least one tick.
  • logInsights only logs when the analysis score drops below 70, to avoid console noise during normal operation.
  • analyzeStore throws if the store does not have AI enabled (hasAI(store) is false). Guard with hasAI(store) if activation is conditional.

Pitfalls

  • Applying @AIPowered without @ReactiveStore is valid but the store will not be in the StoreRegistry. The AI features still work; only registry lookup is affected.
  • @AIPowered attaches AI instances using __analyzer__, __optimizer__, __predictor__ property names. Do not define these properties on your store class or they will be overwritten on the first dispatch.
  • autoAnalyze: true and autoOptimize: true add overhead to every dispatch — typically sub-millisecond for simple states, but measurable for states with hundreds of properties. Set them to false in production for performance-critical stores; use analyzeStore on demand instead.
  • onSuggestion and onAnomaly callbacks are called synchronously inside the patched dispatch. Keep them fast; avoid blocking I/O or heavy computation in these callbacks.
From the canonical UDIOT public documentation.Try it in the playground ↗