simullm
Version:
Event-driven Agent-Based Modeling framework for TypeScript
246 lines (188 loc) • 6.62 kB
Markdown
# 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
```bash
npm install simullm
# or
bun add simullm
```
## Quick Start
```typescript
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()); // 2
```
## Core 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 state
- `agentStates`: All agent internal states
- `lastAction`: The action that was just processed
- `actionCount`: Total number of processed actions
## Examples
### Basic Counter
```typescript
const simulation = createSimulation({
initialGlobalState: { value: 0 },
agents: [counterAgent],
shouldExit: ({ actionCount }) => actionCount >= 5
});
```
### State-based Exit
```typescript
const simulation = createSimulation({
initialGlobalState: { temperature: 20 },
agents: [heatingAgent, coolingAgent],
shouldExit: ({ globalState }) =>
globalState.temperature >= 100 || globalState.temperature <= 0
});
```
### Agent Memory Exit
```typescript
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 state
- `agents: Agent[]` - Array of agents to participate
- `shouldExit: (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 identifier
- `onAction: (action, context) => void | Promise<void>` - Action handler
- `initialInternalState?: TInternalState` - Optional internal state
### `EventSimulation` Methods
- `dispatch(action)` - Dispatch an action to all agents
- `exit()` - Returns a promise that resolves when simulation exits
- `getGlobalState()` - Get current global state
- `getAgentInternalState(agentId)` - Get agent's internal state
- `getAllAgentStates()` - Get all agent internal states
- `getActionCount()` - Get total processed actions
- `hasSimulationExited()` - 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 states
- `dispatch(action)` - Dispatch new actions
- `updateGlobalState(updater)` - Modify global state
- `updateInternalState(updater)` - Modify agent's internal state
### Agent Coordination
Use `context.allAgents` to coordinate between agents:
```typescript
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:
```typescript
// 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):**
```typescript
const simulation = createSimulation({
initialGlobalState: { value: 0 },
agents: [myAgent]
// Could run infinitely
});
```
**After (v0.2.x):**
```typescript
const simulation = createSimulation({
initialGlobalState: { value: 0 },
agents: [myAgent],
shouldExit: ({ actionCount }) => actionCount >= 10 // Required
});
```
## Development
### Release Process
Use the automated release script:
```bash
bun run release
```
This 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