Message
Information quanta — the bits
MessageProcessor
Information transformations — the channels
CompositeProcessor
Information topology — the graph
Everything else in tinychat is derived from these three primitives. Databases, APIs, LLMs, and CRMs are all processors, while conversations and agents are composite processors.
Message: Information Quanta
Messages are the atomic units of information that flow through your system. They represent discrete pieces of data that processors transform and route.Core Characteristics
Messages in tinychat are designed as immutable value objects with the following properties:Immutability
Immutability
Message structure is frozen after creation, ensuring predictable behavior throughout the processing pipeline.
AIMessage.tool_calls uses mutable containers for updates as an acknowledged exception for LLM API compatibility.Lightweight Design
Lightweight Design
Messages prefer bounded structures to keep them efficient and fast to transmit.
AIMessage.tool_calls is an acknowledged exception to support standard LLM API patterns.Explicit Typing
Explicit Typing
No generic dictionaries. Every message has a specific, well-defined type that determines how it’s routed and processed.
Self-Identifying
Self-Identifying
Each message includes metadata like
id and timestamp for tracking and debugging.Semantic Boundaries
tinychat provides semantic markers for system boundaries:MessageProcessor: Information Channels
Processors are stateful transformations that convert one message type into another. Think of them as channels through which information flows and gets transformed.Processing Contract
Every processor implements a simple, powerful contract:The signature
Message → Optional[Message] defines a processor as a potentially noisy channel that can fail (returning None) or succeed (returning a transformed message).Key Properties
Stateful Transformations
Stateful Transformations
Processors maintain their own internal state, allowing them to learn, adapt, and make context-aware decisions across multiple message transformations.
Can Fail (Noisy Channels)
Can Fail (Noisy Channels)
Processors can return
None to signal processing failure or message filtering, modeling real-world noisy communication channels.Capacity Constraints
Capacity Constraints
Processors are asynchronous by default, allowing concurrent I/O operations while respecting system resource bounds.
Lifecycle Management
Processors require explicit setup before use:1
Setup before processing
Call
setup() before calling process() on any processor. This initializes shared resources like the TaskManager and registers observers.2
Shared configuration
SetupConfig provides shared TaskManager and observers across all processors. In recursive composition, parent CompositeProcessor shares the TaskManager with child processors.3
Cleanup when done
Call
cleanup() to properly release resources when you’re finished with a processor.Output Type Declaration
Processors can declare their output types for topology validation:CompositeProcessor: Information Topology
TheCompositeProcessor is where the magic happens. It creates a type-based routing graph that automatically chains message transformations based on message types.
Type-Based Routing
CompositeProcessor routes messages through handlers based on their types, creating a 1:1 mapping from message type to handler:Message flow through a type-based routing topology
Routing Semantics
Message Types → Handlers (1:1)
Message Types → Handlers (1:1)
Each message type maps to exactly one handler. This ensures deterministic routing and prevents ambiguity.
Handlers → Message Types (1:N)
Handlers → Message Types (1:N)
Each handler can produce multiple different message types based on internal state and logic. This creates state-dependent routing paths.
Automatic Chaining
Automatic Chaining
Results are automatically re-routed through handlers until:
- An
EgressMessageis produced (terminal condition) Noneis returned by a processormax_hopsis reached (prevents infinite loops)
Topology Patterns
The 1:N handler-to-types relationship enables rich topology patterns:- Pipeline
- Conditional Branching
- State Machine
- Recursive Composition
Linear chain where each processor produces exactly one message type:
Execution Model
Processing is sequential within a chain but concurrent across chains. One message transformation happens at a time within a single message flow, but multiple independent message flows can be processed concurrently.
Topology Validation
Validate your graph structure at construction time:Terminal types (
None and EgressMessage) are excluded from validation but still shown in topology visualizations.Interruption Control
Stop processing gracefully when needed:Design Philosophy
Simplicity First
Minimal, essential building blocks. No assumptions about caching, rate limiting, or retry logic in core primitives.
Information Theory
Grounded in information theory: messages as quanta, processors as channels, composition as topology.
Composable by Nature
Simple primitives combine to create arbitrarily complex systems. CompositeProcessor is itself a MessageProcessor.
Async by Default
Built for I/O-bound conversational systems. Concurrency is fundamental, not an afterthought.
The framework provides the primitives. You implement the policies. Features like caching, rate limiting, retry logic, circuit breakers, authentication, persistence, and monitoring are user concerns that should be implemented as observers or custom processors.