pinsky-three/llmca

LLMCA (Language Model Cellular Automata) is an experimental project that combines cellular automata with language models (LLM). This project allows simulating a cognitive space where each cell is a cognitive unit evolving based on rules defined by a language model.

Rust

8

104 commits

updated May 30, 2026

See the code

README

LLMCA: Large Language Model Cellular Automata

LLMCA (Language Model Cellular Automata) is an experimental project that combines cellular automata with large language models (LLMs). It simulates a cognitive space where each cell evolves based on rules defined and interpreted by an LLM, considering the state of its neighbors.

Features

  • Cognitive Units with Memory: Each cell acts as a cognitive unit with configurable temporal memory, storing its past states with timestamps for historical awareness.
  • LLM-Driven Evolution: Cells determine their next state by querying an LLM, providing their memory history and their neighbors' current states. The LLM responds with a new state and optionally, an updated rule using structured JSON schemas (CognitiveUnitPair).
  • Von Neumann Neighborhood: Cells interact with their immediate neighbors in a 2D grid using the Von Neumann neighborhood (north, south, east, west, and diagonals).
  • Distributed Computation: Supports distributing computations across multiple LLM API instances for improved performance with parallel task execution.
  • Entity Management System: Built-in LifeManager for managing multiple simulation entities with persistence and lifecycle management.
  • Flexible API Configuration: Support for multiple LLM resolvers via TOML configuration (resolvers.toml) or environment variables, allowing heterogeneous API backends.
  • JSON Schema Integration: Uses schemars for automatic schema generation, ensuring type-safe communication between the simulation and LLM APIs.
  • Visualization: Renders the simulation in real-time using Macroquad, representing cell states with colors derived from hexadecimal strings returned by the LLM.
  • Persistence: Saves the simulation state to disk (in .life directory), allowing resumption from previous steps.

Requirements

  • Rust & Cargo: Ensure you have Rust and Cargo installed.
  • LLM API Access: Requires access to an LLM API compatible with the OpenAI API format (e.g., OpenAI, Ollama). Set up necessary environment variables (see Usage).
  • Macroquad: For visualization.

Installation

  1. Clone: git clone https://github.com/pinsky-three/llmca.git
  2. Build: cd llmca && cargo build

Usage

  1. API Configuration: Configure LLM resolvers using one of two methods:

    Option A: TOML Configuration (Recommended)

    Create a resolvers.toml file in the project root:

    [[resolvers]]
    provider = "ollama"
    api_url = "http://localhost:11434"
    model_name = "llama3.2"
    api_key = "_"
    
    [[resolvers]]
    provider = "openrouter"
    api_url = "https://openrouter.ai/api/v1"
    model_name = "deepseek/deepseek-r1-distill-qwen-1.5b"
    api_key = "your_key"
    

    provider is optional for older configs. If omitted, URLs containing openrouter use the OpenRouter provider; all other URLs default to Ollama. Ollama URLs may be written as http://localhost:11434 or the legacy OpenAI-compatible http://localhost:11434/v1; the native Rig adapter will normalize the latter.

    Option B: Environment Variables

    Create a .env file in the project root:

    OPENAI_API_URL="http://your_api_url:port/v1" # Comma-separated for multiple APIs
    OPENAI_MODEL_NAME="your_model_name" # Comma-separated for multiple models
    OPENAI_API_KEY="your_api_key" # Comma-separated for multiple keys
    OPENAI_PROVIDER="ollama" # Optional, comma-separated: ollama or openrouter
    

    If using multiple APIs, ensure the number of URLs, model names, and API keys match.

  2. Run: cargo run -p minimal-ui

Observability

Runtime binaries initialize structured JSON logs with tracing-subscriber. The default log level is info, which records step-level telemetry without logging every model response:

RUST_LOG=info cargo run -p minimal-ui

Each completed evolution step logs unit count, resolver count, chunks, unique-state count, parse failures, LLM transport failures, and elapsed time. The API also returns the same telemetry from POST /api/entity/:id/evolve. minimal-ui skips saved entity restore on startup and logs manager/entity setup duration separately. Slow evolution chunks are logged at info; fast chunks are logged at debug.

Model calls use Rig instead of a hand-built chat-completions request. Ollama uses Rig's typed structured output path, which sends the CognitiveUnitPair JSON schema through Ollama's native format parameter. OpenRouter uses Rig's extractor/tool path because Rig 0.36 does not yet map its generic output_schema field to OpenRouter's native response_format.

Raw model responses are available behind a targeted debug filter so normal runs avoid high-volume logging and formatting overhead:

RUST_LOG=info,llmca::model_response=debug cargo run -p minimal-ui

Do not enable llmca::model_response=debug when prompts or model outputs may contain sensitive data. API keys are never emitted by the structured logs.

Max in-flight completion requests equals the number of resolvers in resolvers.toml. For example, 4 resolvers means 4 concurrent completion calls.

Simulation Example

The LLM receives a JSON input representing a cell's memory (previous states) and its neighbors' current states. It's instructed to return a JSON object containing the next state and optionally, a new rule following the CognitiveUnitPair schema.

Example LLM System Prompt:

You're a LLM Cognitive Unit and your unique task is to respond with your next (rule, state)
based on your current rule and the states of your neighbors in json format.
Always respond with a plain json compliant with `CognitiveUnitPair` schema.
The user passes your memory and the neighborhood states as a list of 'messages' in json format.
Don't put the json in a code block, don't add explanations, just return the json ready to be parsed.
Only if your rule is empty, you may propose a new rule and return it with the response.
If you think the rule is wrong, you may propose a new rule and return it with the response.
Example of valid response: `{"rule": "rule_1", "state": "state_1"}`

Example LLM Input (Simplified):

[
  "self memory",
  {"rule": "be red if neighbors are green", "state": "#ff0000"},
  {"rule": "be red if neighbors are green", "state": "#ff0000"},
  "neighbors",
  {"rule": "...", "state": "#00ff00"},
  {"rule": "...", "state": "#00ff00"}
]

Example LLM Output:

{"rule": "be red if neighbors are green", "state": "#ff0000"}

The visualization then interprets the state (e.g., #ff0000) as a color. The simulation maintains a temporal memory (configurable size) of past states for each cognitive unit, allowing the LLM to consider historical patterns when determining the next state.

Contributions

Contributions are welcome! Fork the project and submit pull requests.

License

This project is licensed under the MIT License. For more details, see the LICENSE file.

Contributors

bregydoc

104 commits

pinsky-three/llmca

LLMCA (Language Model Cellular Automata) is an experimental project that combines cellular automata with language models (LLM). This project allows simulating a cognitive space where each cell is a cognitive unit evolving based on rules defined by a language model.

Rust

8

104 commits

updated May 30, 2026

See the code

README

LLMCA: Large Language Model Cellular Automata

LLMCA (Language Model Cellular Automata) is an experimental project that combines cellular automata with large language models (LLMs). It simulates a cognitive space where each cell evolves based on rules defined and interpreted by an LLM, considering the state of its neighbors.

Features

  • Cognitive Units with Memory: Each cell acts as a cognitive unit with configurable temporal memory, storing its past states with timestamps for historical awareness.
  • LLM-Driven Evolution: Cells determine their next state by querying an LLM, providing their memory history and their neighbors' current states. The LLM responds with a new state and optionally, an updated rule using structured JSON schemas (CognitiveUnitPair).
  • Von Neumann Neighborhood: Cells interact with their immediate neighbors in a 2D grid using the Von Neumann neighborhood (north, south, east, west, and diagonals).
  • Distributed Computation: Supports distributing computations across multiple LLM API instances for improved performance with parallel task execution.
  • Entity Management System: Built-in LifeManager for managing multiple simulation entities with persistence and lifecycle management.
  • Flexible API Configuration: Support for multiple LLM resolvers via TOML configuration (resolvers.toml) or environment variables, allowing heterogeneous API backends.
  • JSON Schema Integration: Uses schemars for automatic schema generation, ensuring type-safe communication between the simulation and LLM APIs.
  • Visualization: Renders the simulation in real-time using Macroquad, representing cell states with colors derived from hexadecimal strings returned by the LLM.
  • Persistence: Saves the simulation state to disk (in .life directory), allowing resumption from previous steps.

Requirements

  • Rust & Cargo: Ensure you have Rust and Cargo installed.
  • LLM API Access: Requires access to an LLM API compatible with the OpenAI API format (e.g., OpenAI, Ollama). Set up necessary environment variables (see Usage).
  • Macroquad: For visualization.

Installation

  1. Clone: git clone https://github.com/pinsky-three/llmca.git
  2. Build: cd llmca && cargo build

Usage

  1. API Configuration: Configure LLM resolvers using one of two methods:

    Option A: TOML Configuration (Recommended)

    Create a resolvers.toml file in the project root:

    [[resolvers]]
    provider = "ollama"
    api_url = "http://localhost:11434"
    model_name = "llama3.2"
    api_key = "_"
    
    [[resolvers]]
    provider = "openrouter"
    api_url = "https://openrouter.ai/api/v1"
    model_name = "deepseek/deepseek-r1-distill-qwen-1.5b"
    api_key = "your_key"
    

    provider is optional for older configs. If omitted, URLs containing openrouter use the OpenRouter provider; all other URLs default to Ollama. Ollama URLs may be written as http://localhost:11434 or the legacy OpenAI-compatible http://localhost:11434/v1; the native Rig adapter will normalize the latter.

    Option B: Environment Variables

    Create a .env file in the project root:

    OPENAI_API_URL="http://your_api_url:port/v1" # Comma-separated for multiple APIs
    OPENAI_MODEL_NAME="your_model_name" # Comma-separated for multiple models
    OPENAI_API_KEY="your_api_key" # Comma-separated for multiple keys
    OPENAI_PROVIDER="ollama" # Optional, comma-separated: ollama or openrouter
    

    If using multiple APIs, ensure the number of URLs, model names, and API keys match.

  2. Run: cargo run -p minimal-ui

Observability

Runtime binaries initialize structured JSON logs with tracing-subscriber. The default log level is info, which records step-level telemetry without logging every model response:

RUST_LOG=info cargo run -p minimal-ui

Each completed evolution step logs unit count, resolver count, chunks, unique-state count, parse failures, LLM transport failures, and elapsed time. The API also returns the same telemetry from POST /api/entity/:id/evolve. minimal-ui skips saved entity restore on startup and logs manager/entity setup duration separately. Slow evolution chunks are logged at info; fast chunks are logged at debug.

Model calls use Rig instead of a hand-built chat-completions request. Ollama uses Rig's typed structured output path, which sends the CognitiveUnitPair JSON schema through Ollama's native format parameter. OpenRouter uses Rig's extractor/tool path because Rig 0.36 does not yet map its generic output_schema field to OpenRouter's native response_format.

Raw model responses are available behind a targeted debug filter so normal runs avoid high-volume logging and formatting overhead:

RUST_LOG=info,llmca::model_response=debug cargo run -p minimal-ui

Do not enable llmca::model_response=debug when prompts or model outputs may contain sensitive data. API keys are never emitted by the structured logs.

Max in-flight completion requests equals the number of resolvers in resolvers.toml. For example, 4 resolvers means 4 concurrent completion calls.

Simulation Example

The LLM receives a JSON input representing a cell's memory (previous states) and its neighbors' current states. It's instructed to return a JSON object containing the next state and optionally, a new rule following the CognitiveUnitPair schema.

Example LLM System Prompt:

You're a LLM Cognitive Unit and your unique task is to respond with your next (rule, state)
based on your current rule and the states of your neighbors in json format.
Always respond with a plain json compliant with `CognitiveUnitPair` schema.
The user passes your memory and the neighborhood states as a list of 'messages' in json format.
Don't put the json in a code block, don't add explanations, just return the json ready to be parsed.
Only if your rule is empty, you may propose a new rule and return it with the response.
If you think the rule is wrong, you may propose a new rule and return it with the response.
Example of valid response: `{"rule": "rule_1", "state": "state_1"}`

Example LLM Input (Simplified):

[
  "self memory",
  {"rule": "be red if neighbors are green", "state": "#ff0000"},
  {"rule": "be red if neighbors are green", "state": "#ff0000"},
  "neighbors",
  {"rule": "...", "state": "#00ff00"},
  {"rule": "...", "state": "#00ff00"}
]

Example LLM Output:

{"rule": "be red if neighbors are green", "state": "#ff0000"}

The visualization then interprets the state (e.g., #ff0000) as a color. The simulation maintains a temporal memory (configurable size) of past states for each cognitive unit, allowing the LLM to consider historical patterns when determining the next state.

Contributions

Contributions are welcome! Fork the project and submit pull requests.

License

This project is licensed under the MIT License. For more details, see the LICENSE file.

Contributors

bregydoc

104 commits

Languages

Rust

96.7%

Shell

3.3%