@moora/moorex
Version:
A generic asynchronous Moore machine for building persistent AI agents that survive crashes, restarts, or migrations
114 lines (102 loc) • 3.85 kB
text/typescript
import { type Immutable } from 'mutative';
import { createSignalQueue } from './signal-queue';
import { createEventEmitter } from './event-emitter';
import type {
MoorexDefinition,
Moorex,
MoorexEvent,
} from './types';
/**
* 创建一个 Moorex 机器实例。
*
* Moorex 是一个通用的异步 Moore 机器,它:
* 1. 跟踪状态,通过信号触发状态转换
* 2. 根据当前状态计算应该运行的 effects
* 3. 在状态改变时协调 effects(通知哪些应该取消,哪些应该启动)
* 4. 提供事件订阅机制,监听状态和 effect 的变化
*
* Moorex 不再负责执行 effects,只负责根据 reconciliation 结果通知
* effect-start 和 effect-canceled 事件。实际的 effect 执行由外部
* 通过 createEffectRunner 来实现。
*
* 设计用于构建持久化的 AI agents,这些 agents 必须在崩溃、重启或迁移时存活,
* 同时能够恢复未完成的工作。通过重新加载状态并运行 effect 协调,agent 可以
* 从上次中断的地方继续。
*
* @example
* ```typescript
* const definition: MoorexDefinition<State, Signal, Effect> = {
* initiate: () => ({ count: 0 }),
* transition: (signal) => (state) => ({ ...state, count: state.count + 1 }),
* effectsAt: (state) => state.count > 0 ? { 'effect-1': effectData } : {},
* };
*
* const moorex = createMoorex(definition);
*
* // 使用 createEffectRunner 来执行 effects
* const runEffect = (effect, state, key) => ({
* start: async (dispatch) => {
* // 执行 effect 逻辑
* await doSomething();
* // 可以 dispatch 新的信号
* dispatch(someSignal);
* },
* cancel: () => {
* // 取消 effect
* }
* });
*
* moorex.subscribe(createEffectRunner(runEffect));
* moorex.dispatch({ type: 'increment' });
* ```
*
* @template State - 机器的状态类型
* @template Signal - 信号类型,用于触发状态转换
* @template Effect - Effect 类型
* @param definition - Moore 机器的定义配置
* @returns Moorex 机器实例
*/
export const createMoorex = <State, Signal, Effect>(
definition: MoorexDefinition<State, Signal, Effect>,
): Moorex<State, Signal, Effect> => {
const activeEffects = new Map<string, Immutable<Effect>>();
let state: Immutable<State> = definition.initiate();
const { emit, subscribe } = createEventEmitter<
MoorexEvent<State, Signal, Effect>,
Moorex<State, Signal, Effect>
>();
const moorex: Moorex<State, Signal, Effect> = {
dispatch: (signal: Immutable<Signal>) => schedule(signal),
subscribe,
getState: () => state,
};
const reconcileEffects = () => {
const currentEffects = definition.effectsAt(state);
// 取消不再需要的 effects
for (const [key, effect] of [...activeEffects]) {
if (!(key in currentEffects)) {
activeEffects.delete(key);
emit({ type: 'effect-canceled', effect, key }, moorex);
}
}
// 启动新的 effects
for (const [key, effect] of Object.entries(currentEffects)) {
if (!activeEffects.has(key)) {
activeEffects.set(key, effect);
emit({ type: 'effect-started', effect, key }, moorex);
}
}
};
const { schedule } = createSignalQueue<Signal>((signals) => {
// 使用 reduce 累积状态转换
state = signals.reduce((currentState, signal) => {
emit({ type: 'signal-received', signal }, moorex);
return definition.transition(signal)(currentState);
}, state);
reconcileEffects();
emit({ type: 'state-updated', state }, moorex);
});
// 延迟初始 reconciliation 到下一个微任务,确保订阅者有机会注册
queueMicrotask(() => reconcileEffects());
return moorex;
};