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.
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.
# 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+
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.
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
characterandoccupationlabel payloads are preserved. character_full(default) returns the full canonical character+occupation axis set.subset_legacypreserves historical sparse optional-axis behavior as explicit opt-in.axeskeeps the same deterministic normalized score shape for adapter contracts.generator_capabilitiesnow includesnumeric_axis_scoreswhen this dual-format output is available.
The examples/ directory contains comprehensive, runnable examples demonstrating all library features:
basic_usage.py- Simple generation, serialization, and reproducibilityadvanced_usage.py- Weighted distributions, exclusion rules, and statistical analysisintegration_example.py- Combining character and occupation axis systems for complete entity generation
batch_generation.py- Bulk generation with JSON/CSV export and memory-efficient streamingcustom_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
# 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.
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
The library currently provides two independent axis systems:
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.
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
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 # RareThis creates believable populations where most characters are poor or modest.
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.
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.
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 # Truepipeworks_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
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
Weighted distributions reflect population realism:
- Most characters are poor or modest
- Ancient characters are rare
- Sickly and frail conditions cluster at population margins
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.
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.
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.
# 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"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 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.
# 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# 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# 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-filesThis 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.
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.
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).
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.htmlOnce 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.
To update the API documentation, simply edit the docstrings in the Python source files:
- Edit docstrings in
src/condition_axis/*.pyfiles (use Google style) - Build locally to preview changes:
sphinx-build -b html docs docs/_build/html - 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:
- 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
- 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
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.
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.
GPL-3.0
This repository is part of the broader Pipeworks project.
- 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)
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/WindowsThe 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
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:
- Ensure all tests pass:
pytest - Verify code quality:
pre-commit run --all-files - Add tests for new functionality
- Update documentation as needed
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.