forked from genewildish/Mainline
Implement Option 5: Hybrid preset-graph system that combines preset
simplicity with graph flexibility, providing 70% reduction in config
file size compared to verbose node DSL.
## New Files
- engine/pipeline/hybrid_config.py - Core hybrid config parser
- examples/hybrid_config.toml - Example hybrid configuration (20 lines)
- examples/hybrid_visualization.py - Demo script using hybrid config
- tests/test_hybrid_config.py - Comprehensive test suite (17 tests)
- docs/hybrid-config.md - Complete documentation
## Key Features
1. **Concise Syntax** (70% smaller than verbose DSL):
2. **Automatic Connections**: Linear pipeline order is inferred
3. **Flexible Configuration**:
- Inline objects:
- Array notation:
- Shorthand:
4. **Python API**:
- - Load from TOML
- - Convert from preset
- - Convert to pipeline
- - Convert to graph for further manipulation
## Usage
Loading hybrid configuration...
======================================================================
✓ Hybrid config loaded from hybrid_config.toml
Source: headlines
Camera: scroll
Effects: 4
- noise: intensity=0.3
- fade: intensity=0.5
- glitch: intensity=0.2
- firehose: intensity=0.4
Display: terminal
[38;5;226mAuto-injected stages for missing capabilities: ['camera_update', 'render'][0m
✓ Pipeline created with 9 stages
Stages: ['source', 'camera', 'noise', 'fade', 'glitch', 'firehose', 'display', 'camera_update', 'render']
[?25l✓ Pipeline initialized
Executing pipeline...
[2;38;5;34m>[0m [2;38;5;245mMIT Tech Review [38;5;22m.[38;5;22m.[38;5;22m.[38;5;22m.[38;5;22m.[38;5;22m.[38;5;22m.[38;5;22m.[38;5;22m.[38;5;22m.[38;5;22m.[38;5;22m.[38;5;22m.[38;5;22m.[38;5;22m.[38;5;22m.[38;5;22m.[38;5;22m.[38;5;22m.[38;5;22m.[38;5;22m.[38;5;22m.[38;5;22m.[38;5;22m.[38;5;22m.[38;5;22m.[38;5;22m.[38;5;22m.[38;5;22m.[38;5;22m. [38;5;34mLINKED [10][0m
[2;38;5;34m>[0m [2;38;5;245mQuanta [38;5;22m.[38;5;22m.[38;5;22m.[38;5;22m.[38;5;22m.[38;5;22m.[38;5;22m.[38;5;22m.[38;5;22m.[38;5;22m.[38;5;22m.[38;5;22m.[38;5;22m.[38;5;22m.[38;5;22m.[38;5;22m.[38;5;22m.[38;5;22m.[38;5;22m.[38;5;22m.[38;5;22m.[38;5;22m.[38;5;22m.[38;5;22m.[38;5;22m.[38;5;22m.[38;5;22m.[38;5;22m.[38;5;22m.[38;5;22m. [38;5;34mLINKED [5][0m
[2;38;5;34m>[0m [2;38;5;245mPhys.org [38;5;22m.[38;5;22m.[38;5;22m.[38;5;22m.[38;5;22m.[38;5;22m.[38;5;22m.[38;5;22m.[38;5;22m.[38;5;22m.[38;5;22m.[38;5;22m.[38;5;22m.[38;5;22m.[38;5;22m.[38;5;22m.[38;5;22m.[38;5;22m.[38;5;22m.[38;5;22m.[38;5;22m.[38;5;22m.[38;5;22m.[38;5;22m.[38;5;22m.[38;5;22m.[38;5;22m.[38;5;22m.[38;5;22m.[38;5;22m. [38;5;34mLINKED [30][0m
[2;38;5;34m>[0m [2;38;5;245mArs Technica [38;5;22m.[38;5;22m.[38;5;22m.[38;5;22m.[38;5;22m.[38;5;22m.[38;5;22m.[38;5;22m.[38;5;22m.[38;5;22m.[38;5;22m.[38;5;22m.[38;5;22m.[38;5;22m.[38;5;22m.[38;5;22m.[38;5;22m.[38;5;22m.[38;5;22m.[38;5;22m.[38;5;22m.[38;5;22m.[38;5;22m.[38;5;22m.[38;5;22m.[38;5;22m.[38;5;22m.[38;5;22m.[38;5;22m.[38;5;22m. [38;5;34mLINKED [20][0m
[2;38;5;34m>[0m [2;38;5;245mScience Daily [38;5;22m.[38;5;22m.[38;5;22m.[38;5;22m.[38;5;22m.[38;5;22m.[38;5;22m.[38;5;22m.[38;5;22m.[38;5;22m.[38;5;22m.[38;5;22m.[38;5;22m.[38;5;22m.[38;5;22m.[38;5;22m.[38;5;22m.[38;5;22m.[38;5;22m.[38;5;22m.[38;5;22m.[38;5;22m.[38;5;22m.[38;5;22m.[38;5;22m.[38;5;22m.[38;5;22m.[38;5;22m.[38;5;22m.[38;5;22m. [38;5;34mLINKED [60][0m
[2;38;5;34m>[0m [2;38;5;245mNature [38;5;22m.[38;5;22m.[38;5;22m.[38;5;22m.[38;5;22m.[38;5;22m.[38;5;22m.[38;5;22m.[38;5;22m.[38;5;22m.[38;5;22m.[38;5;22m.[38;5;22m.[38;5;22m.[38;5;22m.[38;5;22m.[38;5;22m.[38;5;22m.[38;5;22m.[38;5;22m.[38;5;22m.[38;5;22m.[38;5;22m.[38;5;22m.[38;5;22m.[38;5;22m.[38;5;22m.[38;5;22m.[38;5;22m.[38;5;22m. [38;5;34mLINKED [75][0m
[2;38;5;34m>[0m [2;38;5;245mNew Scientist [38;5;22m.[38;5;22m.[38;5;22m.[38;5;22m.[38;5;22m.[38;5;22m.[38;5;22m.[38;5;22m.[38;5;22m.[38;5;22m.[38;5;22m.[38;5;22m.[38;5;22m.[38;5;22m.[38;5;22m.[38;5;22m.[38;5;22m.[38;5;22m.[38;5;22m.[38;5;22m.[38;5;22m.[38;5;22m.[38;5;22m.[38;5;22m.[38;5;22m.[38;5;22m.[38;5;22m.[38;5;22m.[38;5;22m.[38;5;22m. [38;5;34mLINKED [99][0m
[2;38;5;34m>[0m [2;38;5;245mNASA [38;5;22m.[38;5;22m.[38;5;22m.[38;5;22m.[38;5;22m.[38;5;22m.[38;5;22m.[38;5;22m.[38;5;22m.[38;5;22m.[38;5;22m.[38;5;22m.[38;5;22m.[38;5;22m.[38;5;22m.[38;5;22m.[38;5;22m.[38;5;22m.[38;5;22m.[38;5;22m.[38;5;22m.[38;5;22m.[38;5;22m.[38;5;22m.[38;5;22m.[38;5;22m.[38;5;22m.[38;5;22m.[38;5;22m.[38;5;22m. [38;5;34mLINKED [10][0m
[2;38;5;34m>[0m [2;38;5;245mBBC Business [38;5;22m.[38;5;22m.[38;5;22m.[38;5;22m.[38;5;22m.[38;5;22m.[38;5;22m.[38;5;22m.[38;5;22m.[38;5;22m.[38;5;22m.[38;5;22m.[38;5;22m.[38;5;22m.[38;5;22m.[38;5;22m.[38;5;22m.[38;5;22m.[38;5;22m.[38;5;22m.[38;5;22m.[38;5;22m.[38;5;22m.[38;5;22m.[38;5;22m.[38;5;22m.[38;5;22m.[38;5;22m.[38;5;22m.[38;5;22m. [38;5;34mLINKED [54][0m
[2;38;5;34m>[0m [2;38;5;245mBBC Science [38;5;22m.[38;5;22m.[38;5;22m.[38;5;22m.[38;5;22m.[38;5;22m.[38;5;22m.[38;5;22m.[38;5;22m.[38;5;22m.[38;5;22m.[38;5;22m.[38;5;22m.[38;5;22m.[38;5;22m.[38;5;22m.[38;5;22m.[38;5;22m.[38;5;22m.[38;5;22m.[38;5;22m.[38;5;22m.[38;5;22m.[38;5;22m.[38;5;22m.[38;5;22m.[38;5;22m.[38;5;22m.[38;5;22m.[38;5;22m. [38;5;34mLINKED [36][0m
[2;38;5;34m>[0m [2;38;5;245mMarketWatch [38;5;22m.[38;5;22m.[38;5;22m.[38;5;22m.[38;5;22m.[38;5;22m.[38;5;22m.[38;5;22m.[38;5;22m.[38;5;22m.[38;5;22m.[38;5;22m.[38;5;22m.[38;5;22m.[38;5;22m.[38;5;22m.[38;5;22m.[38;5;22m.[38;5;22m.[38;5;22m.[38;5;22m.[38;5;22m.[38;5;22m.[38;5;22m.[38;5;22m.[38;5;22m.[38;5;22m.[38;5;22m.[38;5;22m.[38;5;22m. [38;5;34mLINKED [10][0m
[2;38;5;34m>[0m [2;38;5;245mNPR [38;5;22m.[38;5;22m.[38;5;22m.[38;5;22m.[38;5;22m.[38;5;22m.[38;5;22m.[38;5;22m.[38;5;22m.[38;5;22m.[38;5;22m.[38;5;22m.[38;5;22m.[38;5;22m.[38;5;22m.[38;5;22m.[38;5;22m.[38;5;22m.[38;5;22m.[38;5;22m.[38;5;22m.[38;5;22m.[38;5;22m.[38;5;22m.[38;5;22m.[38;5;22m.[38;5;22m.[38;5;22m.[38;5;22m.[38;5;22m. [38;5;34mLINKED [10][0m
[2;38;5;34m>[0m [2;38;5;245mEconomist [38;5;22m.[38;5;22m.[38;5;22m.[38;5;22m.[38;5;22m.[38;5;22m.[38;5;22m.[38;5;22m.[38;5;22m.[38;5;22m.[38;5;22m.[38;5;22m.[38;5;22m.[38;5;22m.[38;5;22m.[38;5;22m.[38;5;22m.[38;5;22m.[38;5;22m.[38;5;22m.[38;5;22m.[38;5;22m.[38;5;22m.[38;5;22m.[38;5;22m.[38;5;22m.[38;5;22m.[38;5;22m.[38;5;22m.[38;5;22m. [38;5;34mLINKED [299][0m
[2;38;5;34m>[0m [2;38;5;245mAl Jazeera [38;5;22m.[38;5;22m.[38;5;22m.[38;5;22m.[38;5;22m.[38;5;22m.[38;5;22m.[38;5;22m.[38;5;22m.[38;5;22m.[38;5;22m.[38;5;22m.[38;5;22m.[38;5;22m.[38;5;22m.[38;5;22m.[38;5;22m.[38;5;22m.[38;5;22m.[38;5;22m.[38;5;22m.[38;5;22m.[38;5;22m.[38;5;22m.[38;5;22m.[38;5;22m.[38;5;22m.[38;5;22m.[38;5;22m.[38;5;22m. [38;5;34mLINKED [25][0m
[2;38;5;34m>[0m [2;38;5;245mFrance24 [38;5;22m.[38;5;22m.[38;5;22m.[38;5;22m.[38;5;22m.[38;5;22m.[38;5;22m.[38;5;22m.[38;5;22m.[38;5;22m.[38;5;22m.[38;5;22m.[38;5;22m.[38;5;22m.[38;5;22m.[38;5;22m.[38;5;22m.[38;5;22m.[38;5;22m.[38;5;22m.[38;5;22m.[38;5;22m.[38;5;22m.[38;5;22m.[38;5;22m.[38;5;22m.[38;5;22m.[38;5;22m.[38;5;22m.[38;5;22m. [38;5;34mLINKED [24][0m
[2;38;5;34m>[0m [2;38;5;245mGuardian World [38;5;22m.[38;5;22m.[38;5;22m.[38;5;22m.[38;5;22m.[38;5;22m.[38;5;22m.[38;5;22m.[38;5;22m.[38;5;22m.[38;5;22m.[38;5;22m.[38;5;22m.[38;5;22m.[38;5;22m.[38;5;22m.[38;5;22m.[38;5;22m.[38;5;22m.[38;5;22m.[38;5;22m.[38;5;22m.[38;5;22m.[38;5;22m.[38;5;22m.[38;5;22m.[38;5;22m.[38;5;22m.[38;5;22m.[38;5;22m. [38;5;34mLINKED [45][0m
[2;38;5;34m>[0m [2;38;5;245mBBC World [38;5;22m.[38;5;22m.[38;5;22m.[38;5;22m.[38;5;22m.[38;5;22m.[38;5;22m.[38;5;22m.[38;5;22m.[38;5;22m.[38;5;22m.[38;5;22m.[38;5;22m.[38;5;22m.[38;5;22m.[38;5;22m.[38;5;22m.[38;5;22m.[38;5;22m.[38;5;22m.[38;5;22m.[38;5;22m.[38;5;22m.[38;5;22m.[38;5;22m.[38;5;22m.[38;5;22m.[38;5;22m.[38;5;22m.[38;5;22m. [38;5;34mLINKED [28][0m
[2;38;5;34m>[0m [2;38;5;245mABC Australia [38;5;22m.[38;5;22m.[38;5;22m.[38;5;22m.[38;5;22m.[38;5;22m.[38;5;22m.[38;5;22m.[38;5;22m.[38;5;22m.[38;5;22m.[38;5;22m.[38;5;22m.[38;5;22m.[38;5;22m.[38;5;22m.[38;5;22m.[38;5;22m.[38;5;22m.[38;5;22m.[38;5;22m.[38;5;22m.[38;5;22m.[38;5;22m.[38;5;22m.[38;5;22m.[38;5;22m.[38;5;22m.[38;5;22m.[38;5;22m. [38;5;34mLINKED [23][0m
[2;38;5;34m>[0m [2;38;5;245mDW [38;5;22m.[38;5;22m.[38;5;22m.[38;5;22m.[38;5;22m.[38;5;22m.[38;5;22m.[38;5;22m.[38;5;22m.[38;5;22m.[38;5;22m.[38;5;22m.[38;5;22m.[38;5;22m.[38;5;22m.[38;5;22m.[38;5;22m.[38;5;22m.[38;5;22m.[38;5;22m.[38;5;22m.[38;5;22m.[38;5;22m.[38;5;22m.[38;5;22m.[38;5;22m.[38;5;22m.[38;5;22m.[38;5;22m.[38;5;22m. [38;5;34mLINKED [124][0m
[2;38;5;34m>[0m [2;38;5;245mSmithsonian [38;5;22m.[38;5;22m.[38;5;22m.[38;5;22m.[38;5;22m.[38;5;22m.[38;5;22m.[38;5;22m.[38;5;22m.[38;5;22m.[38;5;22m.[38;5;22m.[38;5;22m.[38;5;22m.[38;5;22m.[38;5;22m.[38;5;22m.[38;5;22m.[38;5;22m.[38;5;22m.[38;5;22m.[38;5;22m.[38;5;22m.[38;5;22m.[38;5;22m.[38;5;22m.[38;5;22m.[38;5;22m.[38;5;22m.[38;5;22m. [38;5;34mLINKED [10][0m
[2;38;5;34m>[0m [2;38;5;245mAeon [38;5;22m.[38;5;22m.[38;5;22m.[38;5;22m.[38;5;22m.[38;5;22m.[38;5;22m.[38;5;22m.[38;5;22m.[38;5;22m.[38;5;22m.[38;5;22m.[38;5;22m.[38;5;22m.[38;5;22m.[38;5;22m.[38;5;22m.[38;5;22m.[38;5;22m.[38;5;22m.[38;5;22m.[38;5;22m.[38;5;22m.[38;5;22m.[38;5;22m.[38;5;22m.[38;5;22m.[38;5;22m.[38;5;22m.[38;5;22m. [38;5;34mLINKED [20][0m
[2;38;5;34m>[0m [2;38;5;245mWired [38;5;22m.[38;5;22m.[38;5;22m.[38;5;22m.[38;5;22m.[38;5;22m.[38;5;22m.[38;5;22m.[38;5;22m.[38;5;22m.[38;5;22m.[38;5;22m.[38;5;22m.[38;5;22m.[38;5;22m.[38;5;22m.[38;5;22m.[38;5;22m.[38;5;22m.[38;5;22m.[38;5;22m.[38;5;22m.[38;5;22m.[38;5;22m.[38;5;22m.[38;5;22m.[38;5;22m.[38;5;22m.[38;5;22m.[38;5;22m. [38;5;34mLINKED [48][0m
[2;38;5;34m>[0m [2;38;5;245mThe Hindu [38;5;22m.[38;5;22m.[38;5;22m.[38;5;22m.[38;5;22m.[38;5;22m.[38;5;22m.[38;5;22m.[38;5;22m.[38;5;22m.[38;5;22m.[38;5;22m.[38;5;22m.[38;5;22m.[38;5;22m.[38;5;22m.[38;5;22m.[38;5;22m.[38;5;22m.[38;5;22m.[38;5;22m.[38;5;22m.[38;5;22m.[38;5;22m.[38;5;22m.[38;5;22m.[38;5;22m.[38;5;22m.[38;5;22m.[38;5;22m. [38;5;34mLINKED [60][0m
[2;38;5;34m>[0m [2;38;5;245mJapan Times [38;5;22m.[38;5;22m.[38;5;22m.[38;5;22m.[38;5;22m.[38;5;22m.[38;5;22m.[38;5;22m.[38;5;22m.[38;5;22m.[38;5;22m.[38;5;22m.[38;5;22m.[38;5;22m.[38;5;22m.[38;5;22m.[38;5;22m.[38;5;22m.[38;5;22m.[38;5;22m.[38;5;22m.[38;5;22m.[38;5;22m.[38;5;22m.[38;5;22m.[38;5;22m.[38;5;22m.[38;5;22m.[38;5;22m.[38;5;22m. [38;5;34mLINKED [29][0m
[2;38;5;34m>[0m [2;38;5;245mNautilus [38;5;22m.[38;5;22m.[38;5;22m.[38;5;22m.[38;5;22m.[38;5;22m.[38;5;22m.[38;5;22m.[38;5;22m.[38;5;22m.[38;5;22m.[38;5;22m.[38;5;22m.[38;5;22m.[38;5;22m.[38;5;22m.[38;5;22m.[38;5;22m.[38;5;22m.[38;5;22m.[38;5;22m.[38;5;22m.[38;5;22m.[38;5;22m.[38;5;22m.[38;5;22m.[38;5;22m.[38;5;22m.[38;5;22m.[38;5;22m. [38;5;34mLINKED [10][0m
[2;38;5;34m>[0m [2;38;5;245mGuardian Culture [38;5;22m.[38;5;22m.[38;5;22m.[38;5;22m.[38;5;22m.[38;5;22m.[38;5;22m.[38;5;22m.[38;5;22m.[38;5;22m.[38;5;22m.[38;5;22m.[38;5;22m.[38;5;22m.[38;5;22m.[38;5;22m.[38;5;22m.[38;5;22m.[38;5;22m.[38;5;22m.[38;5;22m.[38;5;22m.[38;5;22m.[38;5;22m.[38;5;22m.[38;5;22m.[38;5;22m.[38;5;22m.[38;5;22m.[38;5;22m. [38;5;34mLINKED [24][0m
[2;38;5;34m>[0m [2;38;5;245mLiterary Hub [38;5;22m.[38;5;22m.[38;5;22m.[38;5;22m.[38;5;22m.[38;5;22m.[38;5;22m.[38;5;22m.[38;5;22m.[38;5;22m.[38;5;22m.[38;5;22m.[38;5;22m.[38;5;22m.[38;5;22m.[38;5;22m.[38;5;22m.[38;5;22m.[38;5;22m.[38;5;22m.[38;5;22m.[38;5;22m.[38;5;22m.[38;5;22m.[38;5;22m.[38;5;22m.[38;5;22m.[38;5;22m.[38;5;22m.[38;5;22m. [38;5;34mLINKED [10][0m
[2;38;5;34m>[0m [2;38;5;245mThe Conversation [38;5;22m.[38;5;22m.[38;5;22m.[38;5;22m.[38;5;22m.[38;5;22m.[38;5;22m.[38;5;22m.[38;5;22m.[38;5;22m.[38;5;22m.[38;5;22m.[38;5;22m.[38;5;22m.[38;5;22m.[38;5;22m.[38;5;22m.[38;5;22m.[38;5;22m.[38;5;22m.[38;5;22m.[38;5;22m.[38;5;22m.[38;5;22m.[38;5;22m.[38;5;22m.[38;5;22m.[38;5;22m.[38;5;22m.[38;5;22m. [38;5;34mLINKED [48][0m
[2;38;5;34m>[0m [2;38;5;245mThe Marginalian [38;5;22m.[38;5;22m.[38;5;22m.[38;5;22m.[38;5;22m.[38;5;22m.[38;5;22m.[38;5;22m.[38;5;22m.[38;5;22m.[38;5;22m.[38;5;22m.[38;5;22m.[38;5;22m.[38;5;22m.[38;5;22m.[38;5;22m.[38;5;22m.[38;5;22m.[38;5;22m.[38;5;22m.[38;5;22m.[38;5;22m.[38;5;22m.[38;5;22m.[38;5;22m.[38;5;22m.[38;5;22m.[38;5;22m.[38;5;22m. [38;5;34mLINKED [20][0m
[2;38;5;34m>[0m [2;38;5;245mLongreads [38;5;22m.[38;5;22m.[38;5;22m.[38;5;22m.[38;5;22m.[38;5;22m.[38;5;22m.[38;5;22m.[38;5;22m.[38;5;22m.[38;5;22m.[38;5;22m.[38;5;22m.[38;5;22m.[38;5;22m.[38;5;22m.[38;5;22m.[38;5;22m.[38;5;22m.[38;5;22m.[38;5;22m.[38;5;22m.[38;5;22m.[38;5;22m.[38;5;22m.[38;5;22m.[38;5;22m.[38;5;22m.[38;5;22m.[38;5;22m. [38;5;34mLINKED [25][0m
[2;38;5;34m>[0m [2;38;5;245mDer Spiegel [38;5;22m.[38;5;22m.[38;5;22m.[38;5;22m.[38;5;22m.[38;5;22m.[38;5;22m.[38;5;22m.[38;5;22m.[38;5;22m.[38;5;22m.[38;5;22m.[38;5;22m.[38;5;22m.[38;5;22m.[38;5;22m.[38;5;22m.[38;5;22m.[38;5;22m.[38;5;22m.[38;5;22m.[38;5;22m.[38;5;22m.[38;5;22m.[38;5;22m.[38;5;22m.[38;5;22m.[38;5;22m.[38;5;22m.[38;5;22m. [38;5;34mLINKED [19][0m
[2;38;5;34m>[0m [2;38;5;245mAtlas Obscura [38;5;22m.[38;5;22m.[38;5;22m.[38;5;22m.[38;5;22m.[38;5;22m.[38;5;22m.[38;5;22m.[38;5;22m.[38;5;22m.[38;5;22m.[38;5;22m.[38;5;22m.[38;5;22m.[38;5;22m.[38;5;22m.[38;5;22m.[38;5;22m.[38;5;22m.[38;5;22m.[38;5;22m.[38;5;22m.[38;5;22m.[38;5;22m.[38;5;22m.[38;5;22m.[38;5;22m.[38;5;22m.[38;5;22m.[38;5;22m. [38;5;34mLINKED [27][0m
[2;38;5;34m>[0m [2;38;5;245mSCMP [38;5;22m.[38;5;22m.[38;5;22m.[38;5;22m.[38;5;22m.[38;5;22m.[38;5;22m.[38;5;22m.[38;5;22m.[38;5;22m.[38;5;22m.[38;5;22m.[38;5;22m.[38;5;22m.[38;5;22m.[38;5;22m.[38;5;22m.[38;5;22m.[38;5;22m.[38;5;22m.[38;5;22m.[38;5;22m.[38;5;22m.[38;5;22m.[38;5;22m.[38;5;22m.[38;5;22m.[38;5;22m.[38;5;22m.[38;5;22m.[H[JThe Download: OpenAI is building a fully automated researcher, and a psychedelic
pe e r o in e a
- n b an t l r i l
nl ad n co ut n h l h a h t e o d d t r c e
C n ua t m co e s a h a e p s o f nd
h w r o n ec le o e cl r a e
T e D w o h en a o ’s new A ns, and n x - n u a r c s
W t do ne nucl ar r tors ea f w s ?
h Penta o s l nni g or I co p nies o tr in cl s i d t def nse o
T ownl d pe I s S mi t dea , an ok’ CS M ws it
T J lies T a vol d er nt y K i e
Qu nt m y o ap Pi ee n r g rd
T e a h T a E p i y B urve Are ver er
Why u a d Stil t u le W t t ll S uff?
W e e ome ee S s, She S es S ace T e M e o F ac l
[2;38;5;34m [0m [2;38;5;37mウ[0m[2;38;5;238m┋[0m [2;38;5;238m [0m [38;5;22m [0m [2;38;5;37m [0m[2;38;5;238m [0m [2;38;5;34mウ[0m [2;38;5;37mホ[0m [2;38;5;34m [0m [2;38;5;37m [0m [38;5;22m [0m[2;38;5;37m [0m [2;38;5;238mウ[0m[2;38;5;37m [0m[38;5;22m┆[0m [38;5;22m [0m [2;38;5;238m [0m [2;38;5;238m [0m[2;38;5;37m [0m [2;38;5;34mメ[0m [2;38;5;37mキ[0m [2;38;5;238m [0m[2;38;5;34mケ[0m [2;38;5;37m┃[0m[2;38;5;37m [0m [2;38;5;238m [0m[2;38;5;238m [0m
Ligh - s d n u t s ar f cia str r a mi h s f ng o
New resea h exp r s h a ad of i ms' u q e t chnol g s
L mi e j bl k oc ob o por nit s f y ng pe in oas l n
Are hu a a ural vi l nt? ew re arc c ll o - e a s ons
a m l e e r q a s?
New cove e p o s ow stro eil m t a ter t Ge in 8 e e
o a t g 3 a g ye b r b
How DICER cuts microRNAs with single-nucleotide precision [38;5;34mLINKED [50][0m
======================================================================
Visualization Output:
======================================================================
The Download: OpenAI is building a fully automated researcher, and a psychedelic
pe e r o in e a
- n b an t l r i l
nl ad n co ut n h l h a h t e o d d t r c e
C n ua t m co e s a h a e p s o f nd
h w r o n ec le o e cl r a e
T e D w o h en a o ’s new A ns, and n x - n u a r c s
W t do ne nucl ar r tors ea f w s ?
h Penta o s l nni g or I co p nies o tr in cl s i d t def nse o
T ownl d pe I s S mi t dea , an ok’ CS M ws it
T J lies T a vol d er nt y K i e
Qu nt m y o ap Pi ee n r g rd
T e a h T a E p i y B urve Are ver er
Why u a d Stil t u le W t t ll S uff?
W e e ome ee S s, She S es S ace T e M e o F ac l
[2;38;5;34m [0m [2;38;5;37mウ[0m[2;38;5;238m┋[0m [2;38;5;238m [0m [38;5;22m [0m [2;38;5;37m [0m[2;38;5;238m [0m [2;38;5;34mウ[0m [2;38;5;37mホ[0m [2;38;5;34m [0m [2;38;5;37m [0m [38;5;22m [0m[2;38;5;37m [0m [2;38;5;238mウ[0m[2;38;5;37m [0m[38;5;22m┆[0m [38;5;22m [0m [2;38;5;238m [0m [2;38;5;238m [0m[2;38;5;37m [0m [2;38;5;34mメ[0m [2;38;5;37mキ[0m [2;38;5;238m [0m[2;38;5;34mケ[0m [2;38;5;37m┃[0m[2;38;5;37m [0m [2;38;5;238m [0m[2;38;5;238m [0m
Ligh - s d n u t s ar f cia str r a mi h s f ng o
New resea h exp r s h a ad of i ms' u q e t chnol g s
L mi e j bl k oc ob o por nit s f y ng pe in oas l n
Are hu a a ural vi l nt? ew re arc c ll o - e a s ons
a m l e e r q a s?
New cove e p o s ow stro eil m t a ter t Ge in 8 e e
o a t g 3 a g ye b r b
How DICER cuts microRNAs with single-nucleotide precision
======================================================================
✓ Successfully rendered 24 lines
## Comparison
| Format | Lines | Use Case |
|--------|-------|----------|
| Preset | 10 | Simple configs |
| **Hybrid** | **20** | **Most use cases (recommended)** |
| Verbose DSL | 39 | Complex DAGs |
All existing functionality preserved - verbose node DSL still works.
259 lines
7.5 KiB
Python
259 lines
7.5 KiB
Python
"""Hybrid Preset-Graph Configuration System
|
|
|
|
This module provides a configuration format that combines the simplicity
|
|
of presets with the flexibility of graphs.
|
|
|
|
Example:
|
|
[pipeline]
|
|
source = "headlines"
|
|
camera = { mode = "scroll", speed = 1.0 }
|
|
effects = [
|
|
{ name = "noise", intensity = 0.3 },
|
|
{ name = "fade", intensity = 0.5 }
|
|
]
|
|
display = { backend = "terminal" }
|
|
|
|
This is much more concise than the verbose node-based graph DSL while
|
|
providing the same flexibility.
|
|
"""
|
|
|
|
from dataclasses import dataclass, field
|
|
from typing import Any, Dict, List, Optional
|
|
from pathlib import Path
|
|
|
|
from engine.pipeline.graph import Graph, NodeType
|
|
from engine.pipeline.graph_adapter import graph_to_pipeline
|
|
|
|
|
|
@dataclass
|
|
class EffectConfig:
|
|
"""Configuration for a single effect."""
|
|
|
|
name: str
|
|
intensity: float = 1.0
|
|
enabled: bool = True
|
|
params: Dict[str, Any] = field(default_factory=dict)
|
|
|
|
|
|
@dataclass
|
|
class CameraConfig:
|
|
"""Configuration for camera."""
|
|
|
|
mode: str = "scroll"
|
|
speed: float = 1.0
|
|
|
|
|
|
@dataclass
|
|
class DisplayConfig:
|
|
"""Configuration for display."""
|
|
|
|
backend: str = "terminal"
|
|
positioning: str = "mixed"
|
|
|
|
|
|
@dataclass
|
|
class PipelineConfig:
|
|
"""Hybrid pipeline configuration combining preset simplicity with graph flexibility.
|
|
|
|
This format provides a concise way to define pipelines that's 70% smaller
|
|
than the verbose node-based DSL while maintaining full flexibility.
|
|
|
|
Example:
|
|
[pipeline]
|
|
source = "headlines"
|
|
camera = { mode = "scroll", speed = 1.0 }
|
|
effects = [
|
|
{ name = "noise", intensity = 0.3 },
|
|
{ name = "fade", intensity = 0.5 }
|
|
]
|
|
display = { backend = "terminal", positioning = "mixed" }
|
|
"""
|
|
|
|
source: str = "headlines"
|
|
camera: Optional[CameraConfig] = None
|
|
effects: List[EffectConfig] = field(default_factory=list)
|
|
display: Optional[DisplayConfig] = None
|
|
viewport_width: int = 80
|
|
viewport_height: int = 24
|
|
|
|
@classmethod
|
|
def from_preset(cls, preset_name: str) -> "PipelineConfig":
|
|
"""Create PipelineConfig from a preset name.
|
|
|
|
Args:
|
|
preset_name: Name of preset (e.g., "upstream-default")
|
|
|
|
Returns:
|
|
PipelineConfig instance
|
|
"""
|
|
from engine.pipeline import get_preset
|
|
|
|
preset = get_preset(preset_name)
|
|
if not preset:
|
|
raise ValueError(f"Preset '{preset_name}' not found")
|
|
|
|
# Convert preset to PipelineConfig
|
|
effects = [EffectConfig(name=e, intensity=1.0) for e in preset.effects]
|
|
|
|
return cls(
|
|
source=preset.source,
|
|
camera=CameraConfig(mode=preset.camera, speed=preset.camera_speed),
|
|
effects=effects,
|
|
display=DisplayConfig(
|
|
backend=preset.display, positioning=preset.positioning
|
|
),
|
|
viewport_width=preset.viewport_width,
|
|
viewport_height=preset.viewport_height,
|
|
)
|
|
|
|
def to_graph(self) -> Graph:
|
|
"""Convert hybrid config to Graph representation."""
|
|
graph = Graph()
|
|
|
|
# Add source node
|
|
graph.node("source", NodeType.SOURCE, source=self.source)
|
|
|
|
# Add camera node if configured
|
|
if self.camera:
|
|
graph.node(
|
|
"camera",
|
|
NodeType.CAMERA,
|
|
mode=self.camera.mode,
|
|
speed=self.camera.speed,
|
|
)
|
|
|
|
# Add effect nodes
|
|
for effect in self.effects:
|
|
graph.node(
|
|
effect.name,
|
|
NodeType.EFFECT,
|
|
effect=effect.name,
|
|
intensity=effect.intensity,
|
|
enabled=effect.enabled,
|
|
**effect.params,
|
|
)
|
|
|
|
# Add display node
|
|
display_config = self.display or DisplayConfig()
|
|
graph.node(
|
|
"display",
|
|
NodeType.DISPLAY,
|
|
backend=display_config.backend,
|
|
positioning=display_config.positioning,
|
|
)
|
|
|
|
# Create linear connections
|
|
# Build chain: source -> camera -> effects... -> display
|
|
chain = ["source"]
|
|
|
|
if self.camera:
|
|
chain.append("camera")
|
|
|
|
# Add all effects in order
|
|
for effect in self.effects:
|
|
chain.append(effect.name)
|
|
|
|
chain.append("display")
|
|
|
|
# Connect all nodes in chain
|
|
for i in range(len(chain) - 1):
|
|
graph.connect(chain[i], chain[i + 1])
|
|
|
|
return graph
|
|
|
|
def to_pipeline(self, viewport_width: int = 80, viewport_height: int = 24):
|
|
"""Convert to Pipeline instance."""
|
|
graph = self.to_graph()
|
|
return graph_to_pipeline(graph, viewport_width, viewport_height)
|
|
|
|
|
|
def load_hybrid_config(toml_path: str | Path) -> PipelineConfig:
|
|
"""Load hybrid configuration from TOML file.
|
|
|
|
Args:
|
|
toml_path: Path to TOML file
|
|
|
|
Returns:
|
|
PipelineConfig instance
|
|
"""
|
|
import tomllib
|
|
|
|
with open(toml_path, "rb") as f:
|
|
data = tomllib.load(f)
|
|
|
|
return parse_hybrid_config(data)
|
|
|
|
|
|
def parse_hybrid_config(data: Dict[str, Any]) -> PipelineConfig:
|
|
"""Parse hybrid configuration from dictionary.
|
|
|
|
Expected format:
|
|
{
|
|
"pipeline": {
|
|
"source": "headlines",
|
|
"camera": {"mode": "scroll", "speed": 1.0},
|
|
"effects": [
|
|
{"name": "noise", "intensity": 0.3},
|
|
{"name": "fade", "intensity": 0.5}
|
|
],
|
|
"display": {"backend": "terminal"}
|
|
}
|
|
}
|
|
"""
|
|
pipeline_data = data.get("pipeline", {})
|
|
|
|
# Parse camera config
|
|
camera = None
|
|
if "camera" in pipeline_data:
|
|
camera_data = pipeline_data["camera"]
|
|
if isinstance(camera_data, dict):
|
|
camera = CameraConfig(
|
|
mode=camera_data.get("mode", "scroll"),
|
|
speed=camera_data.get("speed", 1.0),
|
|
)
|
|
elif isinstance(camera_data, str):
|
|
camera = CameraConfig(mode=camera_data)
|
|
|
|
# Parse effects list
|
|
effects = []
|
|
if "effects" in pipeline_data:
|
|
effects_data = pipeline_data["effects"]
|
|
if isinstance(effects_data, list):
|
|
for effect_item in effects_data:
|
|
if isinstance(effect_item, dict):
|
|
effects.append(
|
|
EffectConfig(
|
|
name=effect_item.get("name", ""),
|
|
intensity=effect_item.get("intensity", 1.0),
|
|
enabled=effect_item.get("enabled", True),
|
|
params=effect_item.get("params", {}),
|
|
)
|
|
)
|
|
elif isinstance(effect_item, str):
|
|
effects.append(EffectConfig(name=effect_item))
|
|
|
|
# Parse display config
|
|
display = None
|
|
if "display" in pipeline_data:
|
|
display_data = pipeline_data["display"]
|
|
if isinstance(display_data, dict):
|
|
display = DisplayConfig(
|
|
backend=display_data.get("backend", "terminal"),
|
|
positioning=display_data.get("positioning", "mixed"),
|
|
)
|
|
elif isinstance(display_data, str):
|
|
display = DisplayConfig(backend=display_data)
|
|
|
|
# Parse viewport settings
|
|
viewport_width = pipeline_data.get("viewport_width", 80)
|
|
viewport_height = pipeline_data.get("viewport_height", 24)
|
|
|
|
return PipelineConfig(
|
|
source=pipeline_data.get("source", "headlines"),
|
|
camera=camera,
|
|
effects=effects,
|
|
display=display,
|
|
viewport_width=viewport_width,
|
|
viewport_height=viewport_height,
|
|
)
|