simullm
v0.3.0
Published
Event-driven Agent-Based Modeling framework for TypeScript
Maintainers
Readme
simullm
Event-driven Agent-Based Modeling framework for TypeScript.
Features
- Event-driven architecture: Agents respond to actions/messages in a reactive system
- Type-safe: Full TypeScript support with generic types for state and actions
- Flexible state management: Both global state and agent internal state
- Exit conditions: Required termination logic prevents infinite loops
- Async support: Handles both synchronous and asynchronous agent actions
- Action cascading: Agents can dispatch new actions during processing
- Memory management: Agents can maintain internal state across turns
Installation
npm install simullm
# or
bun add simullmQuick Start
import { createSimulation, createAgent } from 'simullm';
// Define your state and action types
interface CounterState {
value: number;
}
type CounterAction =
| { type: "INCREMENT"; amount: number }
| { type: "RESET" };
// Create agents
const counterAgent = createAgent<CounterState, CounterAction>(
"counter",
(action, context) => {
if (action.type === "INCREMENT") {
context.updateGlobalState(state => ({
value: state.value + action.amount
}));
} else if (action.type === "RESET") {
context.updateGlobalState(state => ({ value: 0 }));
}
}
);
// Create simulation with required exit condition
const simulation = createSimulation<CounterState, CounterAction>({
initialGlobalState: { value: 0 },
agents: [counterAgent],
shouldExit: ({ actionCount, globalState }) =>
actionCount >= 10 || globalState.value >= 100
});
// Run simulation
await simulation.dispatch({ type: "INCREMENT", amount: 5 });
await simulation.dispatch({ type: "INCREMENT", amount: 15 });
console.log(simulation.getGlobalState()); // { value: 20 }
console.log(simulation.getActionCount()); // 2Core Concepts
Agents
Agents are reactive entities that respond to actions. They can:
- Update global state
- Update their own internal state
- Dispatch new actions
- Maintain memory across turns
- Coordinate with other agents via
context.allAgents
Actions
Actions are messages passed through the system. All agents receive every action and can choose to respond.
Exit Conditions (Required)
Every simulation must define when to stop via the shouldExit function. You have access to:
globalState: Current global stateagentStates: All agent internal stateslastAction: The action that was just processedactionCount: Total number of processed actions
Examples
Basic Counter
const simulation = createSimulation({
initialGlobalState: { value: 0 },
agents: [counterAgent],
shouldExit: ({ actionCount }) => actionCount >= 5
});State-based Exit
const simulation = createSimulation({
initialGlobalState: { temperature: 20 },
agents: [heatingAgent, coolingAgent],
shouldExit: ({ globalState }) =>
globalState.temperature >= 100 || globalState.temperature <= 0
});Agent Memory Exit
const simulation = createSimulation({
initialGlobalState: { score: 0 },
agents: [learningAgent],
shouldExit: ({ agentStates }) =>
agentStates["learner"]?.experience >= 1000
});API Reference
createSimulation<TGlobalState, TAction>(config)
Creates a new simulation instance.
Config:
initialGlobalState: TGlobalState- Starting global stateagents: Agent[]- Array of agents to participateshouldExit: (context: ExitContext) => boolean- Required exit condition
Returns: EventSimulation<TGlobalState, TAction>
createAgent<TGlobalState, TAction, TInternalState>(id, onAction, initialInternalState?)
Creates a new agent.
Parameters:
id: string- Unique agent identifieronAction: (action, context) => void | Promise<void>- Action handlerinitialInternalState?: TInternalState- Optional internal state
EventSimulation Methods
dispatch(action)- Dispatch an action to all agentsexit()- Returns a promise that resolves when simulation exitsgetGlobalState()- Get current global stategetAgentInternalState(agentId)- Get agent's internal stategetAllAgentStates()- Get all agent internal statesgetActionCount()- Get total processed actionshasSimulationExited()- Check if simulation has terminated
Advanced Usage
Agent Context Properties
Each agent receives a context object with:
globalState- Current global state (read-only)internalState- Agent's private state (read-only)allAgents- Array of all agents with their IDs and internal statesdispatch(action)- Dispatch new actionsupdateGlobalState(updater)- Modify global stateupdateInternalState(updater)- Modify agent's internal state
Agent Coordination
Use context.allAgents to coordinate between agents:
const coordinator = createAgent("coordinator", (action, context) => {
// Find available workers
const availableWorkers = context.allAgents
.filter(agent => agent.id !== "coordinator" && !agent.internalState?.busy)
.map(agent => agent.id);
// Assign tasks to available workers
availableWorkers.forEach(workerId => {
context.dispatch({ type: "ASSIGN_TASK", agentId: workerId });
});
});Waiting for Simulation Completion
Use the exit() method to wait for simulations to complete:
// Start simulation
simulation.dispatch({ type: "START" });
// Wait for completion
await simulation.exit();
console.log("Simulation completed!");
console.log("Final state:", simulation.getGlobalState());Examples
See the /experiments directory for complete examples:
- Counter simulation with turn-based agents
- Predator-prey ecosystem simulation
- Market trading simulation with LLM agents
Migration from v0.1.x
Breaking Change: The shouldExit property is now required in SimulationConfig.
Before (v0.1.x):
const simulation = createSimulation({
initialGlobalState: { value: 0 },
agents: [myAgent]
// Could run infinitely
});After (v0.2.x):
const simulation = createSimulation({
initialGlobalState: { value: 0 },
agents: [myAgent],
shouldExit: ({ actionCount }) => actionCount >= 10 // Required
});Development
Release Process
Use the automated release script:
bun run releaseThis handles:
- Version bumping (patch/minor/major)
- Changelog updates
- Git commit and tagging
- Pushing to remote
- npm publishing
See scripts/README.md for details.
License
MIT
