Skip to content

pipe-works/pipeworks_entity_state_generation

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

122 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

CI codecov License: GPL v3

Pipeworks Conditional Axis

A structured, rule-based Python framework for generating coherent character and entity state descriptions across multiple semantic dimensions. Designed for procedural content generation in both visual contexts (AI image prompts) and narrative systems (game development, MUDs, interactive fiction).

Core Philosophy: Conditions exist on axes (e.g., Stable ↔ Precarious) rather than binary flags. The system asks "Where along this axis does interpretation tilt?" rather than "Do you have the condition?" This modulates resolution margins, biases outcomes, and colors narrative interpretation without prescribing specific outcomes.


What This Library Does

This library generates structured, interpretable state for entities during generation time.

It is not a rendering system, a simulation engine, or a narrative framework.

Its sole responsibility is to answer the question:

"What is the state of this thing, and where does it behave slightly off-pattern?"

State is expressed through conditional axes—structured, population-weighted descriptors that bias probability and interpretation without dictating outcomes.


Installation

# Install from PyPI (when published)
pip install pipeworks-conditional-axis

# Install from source for development
git clone https://github.com/pipe-works/pipeworks_entity_state_generation.git
cd pipeworks_entity_state_generation
pip install -e ".[dev]"

Requirements: Python 3.12+


Quick Start

from condition_axis import (
    generate_condition,
    generate_occupation_condition,
    condition_to_prompt,
    occupation_condition_to_prompt,
)

# Generate character physical and social state (may include facial signals)
character_state = generate_condition(seed=42)
print(character_state)
# {'physique': 'wiry', 'wealth': 'poor', 'demeanor': 'suspicious'}

# Generate occupation characteristics
occupation_state = generate_occupation_condition(seed=42)
print(occupation_state)
# {'legitimacy': 'tolerated', 'visibility': 'hidden', 'moral_load': 'neutral'}

# Convert to comma-separated prompts (for image generation, text, etc.)
char_prompt = condition_to_prompt(character_state)
occ_prompt = occupation_condition_to_prompt(occupation_state)

full_prompt = f"{char_prompt}, {occ_prompt}"
print(full_prompt)
# "wiry, poor, suspicious, tolerated, hidden, neutral"

Note: Facial signals are integrated into generate_condition() as an optional axis. The standalone generate_facial_condition() function was removed in v0.10.0 when facial conditions were merged into the unified character system for better cross-system exclusion rules.

Entity API Contract (Web App)

When the API surface is enabled, POST /api/entity returns both legacy label groups and canonical numeric axis scores. Default API behavior uses axis_profile="character_full" so all canonical character+occupation axes are emitted:

{
  "seed": 42,
  "axis_profile": "character_full",
  "character": {
    "physique": "wiry",
    "wealth": "poor",
    "health": "hale",
    "demeanor": "alert",
    "age": "old",
    "facial_signal": "weathered"
  },
  "occupation": {
    "legitimacy": "tolerated",
    "visibility": "hidden",
    "moral_load": "neutral",
    "dependency": "necessary",
    "risk_exposure": "straining"
  },
  "axes": {
    "physique": {"label": "wiry", "score": 0.6},
    "visibility": {"label": "hidden", "score": 0.0}
  },
  "generator_version": "0.11.2",
  "generator_capabilities": [
    "character_conditions",
    "occupation_conditions",
    "seeded_generation",
    "numeric_axis_scores",
    "prompt_serialization"
  ]
}

Compatibility guarantees:

  • Existing character and occupation label payloads are preserved.
  • character_full (default) returns the full canonical character+occupation axis set.
  • subset_legacy preserves historical sparse optional-axis behavior as explicit opt-in.
  • axes keeps the same deterministic normalized score shape for adapter contracts.
  • generator_capabilities now includes numeric_axis_scores when this dual-format output is available.

Usage Examples

The examples/ directory contains comprehensive, runnable examples demonstrating all library features:

Core Examples

  • basic_usage.py - Simple generation, serialization, and reproducibility
  • advanced_usage.py - Weighted distributions, exclusion rules, and statistical analysis
  • integration_example.py - Combining character and occupation axis systems for complete entity generation

Advanced Examples

  • batch_generation.py - Bulk generation with JSON/CSV export and memory-efficient streaming
  • custom_axes.py - Creating custom axis systems (includes fantasy magic and sci-fi tech examples)
  • image_prompt_generation.py - Integration with Stable Diffusion, DALL-E, and Midjourney

Running Examples

# Run any example directly
python examples/basic_usage.py
python examples/integration_example.py
python examples/image_prompt_generation.py

# All examples include:
# - Comprehensive type hints and docstrings
# - Working, executable code with main() functions
# - Educational comments explaining concepts
# - Multiple examples per file (5-7 examples each)

Each example is fully tested (39 tests in tests/test_examples.py) and demonstrates best practices for using the library.


What Are Conditional Axes?

Conditional axes describe the current lived state of an entity.

They are:

  • Mutually exclusive within an axis: A character can't be both "wealthy" and "poor"
  • Population-weighted: Poor characters are more common than wealthy ones
  • Explainable and auditable: Every value comes from a traceable rule
  • Resolved once during generation: Deterministic given the same seed

Available Axis Systems

The library currently provides two independent axis systems:

1. Character Conditions (character_conditions)

Physical and social states that establish baseline character presentation:

  • Physique: frail, hunched, skinny, wiry, broad, stocky
  • Wealth: poor, modest, well-kept, wealthy, decadent
  • Health: sickly, limping, weary, scarred, hale
  • Demeanor: timid, suspicious, resentful, alert, proud
  • Age: young, middle-aged, old, ancient
  • Facial Signal: understated, pronounced, exaggerated, asymmetrical, weathered, soft-featured, sharp-featured

Note on Facial Signals: Previously available as a separate facial_conditions module, facial signals are now integrated into the character conditions system. This allows for cross-system exclusion rules and more coherent character generation.

2. Occupation Conditions (occupation_axis)

Labor pressures and social positioning (not job titles):

  • Legitimacy: sanctioned, tolerated, questioned, illicit
  • Visibility: hidden, discrete, routine, conspicuous
  • Moral Load: neutral, burdened, conflicted, corrosive
  • Dependency: optional, useful, necessary, unavoidable
  • Risk Exposure: benign, straining, hazardous, eroding

Key Features

Weighted Probability Distributions

Axes use realistic population weights rather than uniform randomness:

# Wealth distribution (from WEIGHTS)
"poor": 4.0      # Most common
"modest": 3.0
"well-kept": 2.0
"wealthy": 1.0
"decadent": 0.5  # Rare

This creates believable populations where most characters are poor or modest.

Semantic Exclusion Rules

The system prevents illogical combinations through exclusion rules:

Within-system exclusions:

  • Decadent characters can't be frail or sickly (wealth enables healthcare)
  • Ancient characters aren't timid (age brings confidence)
  • Broad, strong physiques don't pair with sickness
  • Hale (healthy) characters shouldn't have frail physiques

Cross-system exclusions (Character + Facial):

  • Young characters can't have weathered faces (youth vs. wear)
  • Ancient characters rarely have understated features (age is pronounced)
  • Hale (healthy) characters don't look weathered (health affects appearance)
  • Sickly characters don't have soft-featured faces (redundant signal)
  • Decadent wealth prevents weathered appearance (wealth preserves)

Exclusions are applied after random selection, removing conflicts rather than preventing selection. This allows for transparent debugging and maintains generative variety.

Mandatory and Optional Axes

Axes are categorized as mandatory (always included) or optional (conditionally included):

  • Character conditions: Physique and wealth are mandatory; health, demeanor, age, and facial_signal are optional (0-2 selected)
  • Occupation conditions: All five axes are mandatory

This prevents prompt dilution while maintaining narrative clarity.

Reproducible Generation

All generation functions accept an optional seed parameter for deterministic output:

# Same seed = same result
char1 = generate_condition(seed=42)
char2 = generate_condition(seed=42)
assert char1 == char2  # True

Repository Structure

pipeworks_entity_state_generation/
├── README.md                    # This file
├── CLAUDE.md                    # AI assistant development guide
├── LICENSE                      # GPL-3.0
├── pyproject.toml              # Package configuration
├── pytest.ini                  # Test configuration
│
├── src/condition_axis/         # Main package
│   ├── __init__.py             # Public API exports
│   ├── _base.py                # Shared utilities
│   ├── character_conditions.py # Physical, social & facial states (unified)
│   └── occupation_axis.py      # Occupation characteristics
│
├── tests/                      # Test suite (90%+ coverage)
│   ├── test_character_conditions_axis.py
│   ├── test_facial_conditions_axis.py
│   ├── test_occupation_axis_axis.py
│   └── test_examples.py        # Example script tests (39 tests)
│
├── examples/                   # Usage examples (see examples/README.md)
│   ├── README.md               # Comprehensive examples guide
│   ├── basic_usage.py          # Simple generation & serialization
│   ├── advanced_usage.py       # Weights, exclusions & analysis
│   ├── integration_example.py  # Combining character & occupation systems
│   ├── batch_generation.py     # Bulk generation & exports
│   ├── custom_axes.py          # Creating custom axis systems
│   └── image_prompt_generation.py  # AI image generation integration
│
├── docs/                       # Sphinx documentation (autodoc)
│   ├── index.rst               # Documentation root
│   ├── conf.py                 # Sphinx configuration
│   ├── api/                    # Auto-generated API reference
│   │   ├── index.rst           # API overview
│   │   ├── _base.rst           # Core utilities (from docstrings)
│   │   ├── character_conditions.rst  # Character generation (from docstrings)
│   │   └── occupation_axis.rst # Occupation generation (from docstrings)
│   ├── _static/                # Sphinx static assets
│   ├── _templates/             # Sphinx templates
│   └── _build/                 # Generated HTML (git-ignored)
│
└── .github/workflows/          # CI/CD
    ├── ci.yml                  # Unified CI workflow (test, lint, security, docs, build)
    └── publish.yml             # PyPI publishing

Design Principles

Interpretation Over Prescription

Conditions bias interpretation rather than dictate outcomes:

  • "weary" suggests fragility, hesitation, or cost—it doesn't prevent action
  • "wealthy" biases confidence and access—it doesn't guarantee success

Bias Over Randomness

Weighted distributions reflect population realism:

  • Most characters are poor or modest
  • Ancient characters are rare
  • Sickly and frail conditions cluster at population margins

Structure With Room for Failure

Exclusion rules prevent nonsense, but edge cases are allowed:

  • A "decadent" character might be "scarred" (past injury despite current wealth)
  • An "ancient" character can be "limping" (age brings wear)

The system aims for coherence, not perfection.

Explainable State, Inexplicable Detail

Generated state is fully traceable (seed, weights, exclusions), but the why remains interpretive:

  • Why is this character suspicious? The axis doesn't say—narrative fills the gap.
  • Why is this occupation burdensome? The pressure exists, the story provides context.

What This Repository Is Not

This repository does not:

  • Render text or images
  • Simulate behavior over time
  • Implement progression systems
  • Resolve narrative outcomes
  • Balance gameplay
  • Define UI or player interaction

Those concerns belong downstream. This library produces generation-time primitives for consumption by rendering, simulation, and narrative systems.


Advanced Usage

Combining Multiple Axis Systems

# Generate complete character (may include facial signals)
character = generate_condition(seed=123)
occupation = generate_occupation_condition(seed=123)

# Serialize for image prompt
image_prompt = (
    f"illustration of a pale blue-green goblin, "
    f"{condition_to_prompt(character)}, "
    f"whose work operates under the following conditions: "
    f"{occupation_condition_to_prompt(occupation)}"
)
# Example output:
# "illustration of a pale blue-green goblin, skinny, poor, limping, alert,
#  whose work operates under the following conditions: tolerated, discrete, burdened"

Inspecting Available Axes and Values

from condition_axis import (
    get_available_axes,
    get_axis_values,
    CONDITION_AXES,
    WEIGHTS,
)

# List all character condition axes
print(get_available_axes())
# ['physique', 'wealth', 'health', 'demeanor', 'age', 'facial_signal']

# Get possible values for an axis
print(get_axis_values('wealth'))
# ['poor', 'modest', 'well-kept', 'wealthy', 'decadent']

# Access raw data structures
print(CONDITION_AXES['physique'])
# ['frail', 'hunched', 'skinny', 'wiry', 'broad', 'stocky']

print(WEIGHTS['wealth'])
# {'poor': 4.0, 'modest': 3.0, 'well-kept': 2.0, 'wealthy': 1.0, 'decadent': 0.5}

Cross-System Validation

Cross-system exclusion rules are implemented between character and facial axes:

  • age="young" + facial_signal="weathered" → Excluded (contradiction)
  • wealth="decadent" + facial_signal="weathered" → Excluded (wealth preserves appearance)
  • health="hale" + facial_signal="weathered" → Excluded (health affects appearance)

Future work:

  • Cross-validation with occupation axes (e.g., demeanor="timid" + visibility="conspicuous")
  • Weighted cross-system preferences (soft constraints vs. hard exclusions)

See CLAUDE.md for extension guidelines.


Development

Setup

# Clone repository
git clone https://github.com/pipe-works/pipeworks_entity_state_generation.git
cd pipeworks_entity_state_generation

# Create virtual environment
python -m venv venv
source venv/bin/activate  # On Windows: venv\Scripts\activate

# Install with development dependencies
pip install -e ".[dev]"

# Install pre-commit hooks
pre-commit install

Testing

# Run all tests
pytest

# Run with coverage report
pytest --cov=condition_axis --cov-report=term-missing

# Run specific test file
pytest tests/test_character_conditions_axis.py -v

Code Quality

# Format code (line length: 100)
black src/ tests/ --line-length=100

# Lint with auto-fix
ruff check src/ tests/ --line-length=100 --fix

# Type check
mypy src/ --python-version=3.12 --ignore-missing-imports

# Run all pre-commit hooks
pre-commit run --all-files

Building Documentation

This project uses Sphinx with autodoc to automatically generate API documentation from Python docstrings. This approach provides:

  • Zero maintenance burden - Documentation stays in sync with code automatically
  • Always accurate - Content is extracted directly from source code
  • Professional output - Clean HTML with search, indexing, and cross-references

The documentation focuses exclusively on the API reference (modules, functions, classes, parameters). Design philosophy and architectural discussions are maintained in this README and CLAUDE.md to keep them easily accessible.

What is Sphinx?

Sphinx is Python's standard documentation generator that extracts documentation from your code's docstrings and renders it as beautiful, searchable HTML. The autodoc extension automatically pulls docstrings from the source code, eliminating manual documentation maintenance.

What is ReadTheDocs?

ReadTheDocs is a free hosting service that automatically builds and publishes your documentation whenever you push to GitHub. It provides versioning, search, and multiple output formats (HTML, PDF, ePub).

Building Documentation Locally

To build and preview the documentation on your computer:

# Install documentation dependencies
pip install -e ".[docs]"

# Build HTML documentation
cd docs
sphinx-build -b html . _build/html

# View the built documentation
# Open docs/_build/html/index.html in your web browser
# On macOS:
open _build/html/index.html
# On Linux:
xdg-open _build/html/index.html
# On Windows:
start _build/html/index.html

Accessing Online Documentation

Once this repository is connected to ReadTheDocs, the documentation will be available at:

  • Latest version: https://pipeworks-conditional-axis.readthedocs.io/en/latest/
  • Stable version: https://pipeworks-conditional-axis.readthedocs.io/en/stable/

The documentation updates automatically whenever changes are pushed to the main branch.

Contributing to Documentation

To update the API documentation, simply edit the docstrings in the Python source files:

  1. Edit docstrings in src/condition_axis/*.py files (use Google style)
  2. Build locally to preview changes: sphinx-build -b html docs docs/_build/html
  3. Push to GitHub - ReadTheDocs will automatically rebuild and deploy

The documentation is 100% auto-generated from code - no manual .md or .rst files to maintain (except the minimal Sphinx configuration files).

For Sphinx and autodoc syntax, see:


Future Work

Current Features ✅

  • Cross-system exclusion rules: Character and facial axis exclusions implemented (young + weathered, wealth + weathered, etc.)
  • Facial signal integration: Integrated into character_conditions with full coherence rules

Planned Enhancements

  • Extended cross-system exclusions: Validate compatibility between character and occupation axes (e.g., demeanor="timid" + visibility="conspicuous")
  • Unified generator: Single function to generate complete entity state with cross-system coherence
  • Quirks system: Persistent, localized irregularities that introduce structured deviation (see "Quirks" below)
  • Serialization/deserialization: JSON and YAML support for storing and reloading states
  • Condition history tracking: Ledger-based system for tracking state evolution over time

The Quirks System (Planned)

Future versions will introduce quirks—small, persistent deviations that:

  • Remain local to an entity
  • Do not resolve into system-wide rules
  • Bias attention and interpretation
  • Repeat without fully explaining themselves

Quirks will answer:

"Where does this entity fail to behave like a clean model?"

They will complicate situations but must never resolve them. Quirks will be intentionally orthogonal to conditional axes:

  • Axes resolve state; quirks annotate state
  • Axes bias probability; quirks bias attention
  • Quirks must not influence axis resolution or weighting

This separation ensures axes push toward coherence while quirks prevent the system from becoming too clean.


Integration with Pipeworks Ecosystem

This library is part of the broader Pipeworks project:

Repository Role Relationship to This Library
pipeworks-artefact Canonical registry and memory layer Stores generated states and provides persistent identity
pipeworks_entity_state_generation Generation engine (this repo) Produces entity state snapshots
pipeworks_mud_server Interactive runtime and game logic Consumes entity states during play
pipeworks_image_generator Visualization and image synthesis Interprets entity states for visual representation
the_daily_undertaking_ui Narrative and user-facing interface Surfaces entity states to players

This library serves as the pure, stateless generation layer that produces coherent state descriptions consumed by the other components.


License

GPL-3.0

This repository is part of the broader Pipeworks project.


Documentation

Main Documentation

  • README.md - This file (project overview, quick start, usage examples)
  • CLAUDE.md - Development guide for AI assistants and contributors
  • examples/README.md - Comprehensive guide to usage examples
  • API Reference - Auto-generated from docstrings (ReadTheDocs)

Local API Documentation

Build the Sphinx documentation locally to browse the complete API reference:

pip install -e ".[docs]"
cd docs
sphinx-build -b html . _build/html
open _build/html/index.html  # or xdg-open / start on Linux/Windows

The auto-generated documentation includes:

  • condition_axis._base - Core utilities (weighted_choice, apply_exclusion_rules, values_to_prompt)
  • condition_axis.character_conditions - Physical & social character state generation (includes facial signals)
  • condition_axis.occupation_axis - Occupation characteristics generation

Contributing

Contributions are welcome! Please see CLAUDE.md for:

  • Code architecture and patterns
  • Testing philosophy
  • Code style guidelines (Black, Ruff, MyPy)
  • How to add new axes or condition systems

Before submitting a PR:

  1. Ensure all tests pass: pytest
  2. Verify code quality: pre-commit run --all-files
  3. Add tests for new functionality
  4. Update documentation as needed

Status

This library is in active development (v0.10.0 beta).

Core generation systems (character with integrated facial signals, occupation) are stable and well-tested.

Interfaces, schemas, and axis definitions may evolve, but the core separation between state resolution (axes) and state interpretation (downstream systems) is considered foundational.

About

The intent is not to define what is true about a character, system, or situation, but to bias how outcomes are interpreted and resolved.

Resources

License

Stars

Watchers

Forks

Packages

 
 
 

Contributors

Languages