Quickstart Examples
Three ultra-minimal examples to get a feel for StreamBlocks in a few minutes. Each runs offline against a simulated stream; no API keys required. Continue with the basics examples afterwards.
Hello World
The simplest working example: register a block type, process a text stream, and react to BlockEndEvent when a complete block is extracted.
src/hother/streamblocks_examples/00_quickstart/01_hello_world.py
View source on GitHub
#!/usr/bin/env python3
import asyncio
from hother.streamblocks import Registry, StreamBlockProcessor
from hother.streamblocks.core.types import BlockEndEvent
from hother.streamblocks_examples.blocks.agent.files import FileOperations
from hother.streamblocks_examples.helpers.simulator import simple_text_stream
async def main() -> None:
"""Extract a single block from a text stream."""
# Setup: registry + processor
registry = Registry()
registry.register("files_operations", FileOperations)
processor = StreamBlockProcessor(registry)
# Text with one block
text = "!!block01:files_operations\nsrc/main.py:C\n!!end"
stream = simple_text_stream(text)
# Process and extract blocks
async for event in processor.process_stream(stream):
if isinstance(event, BlockEndEvent):
block = event.get_block()
if block:
print("Extracted block:")
print(block.model_dump_json(indent=2))
if __name__ == "__main__":
asyncio.run(main())
Basic Stream
Processes text arriving in chunks, distinguishing extracted blocks from surrounding free text via TextContentEvent. This is the core streaming loop you will use everywhere.
src/hother/streamblocks_examples/00_quickstart/02_basic_stream.py
View source on GitHub
#!/usr/bin/env python3
import asyncio
from hother.streamblocks import Registry, StreamBlockProcessor
from hother.streamblocks.core.types import BlockEndEvent, TextContentEvent
from hother.streamblocks_examples.blocks.agent.files import FileOperations
from hother.streamblocks_examples.helpers.simulator import simulated_stream
async def main() -> None:
"""Process a chunked text stream."""
registry = Registry()
registry.register("files_operations", FileOperations)
processor = StreamBlockProcessor(registry)
# Text to stream in chunks
text = "Some text before.\n!!block:files_operations\napp.py:C\n!!end\nSome text after."
stream = simulated_stream(text, preset="fast")
async for event in processor.process_stream(stream):
if isinstance(event, TextContentEvent):
print(f"[TEXT] {event.content.strip()}")
elif isinstance(event, BlockEndEvent):
block = event.get_block()
if block:
print("\n[BLOCK] Extracted:")
print(block.model_dump_json(indent=2))
if __name__ == "__main__":
asyncio.run(main())
Custom Block
Defines a custom block type with its own metadata and content models, then extracts it with DelimiterFrontmatterSyntax. The key takeaway: a block is just a pair of Pydantic models registered under a type name.
src/hother/streamblocks_examples/00_quickstart/03_custom_block.py
View source on GitHub
#!/usr/bin/env python3
import asyncio
from hother.streamblocks import DelimiterFrontmatterSyntax, Registry, StreamBlockProcessor
from hother.streamblocks.core.models import Block
from hother.streamblocks.core.types import BaseContent, BaseMetadata, BlockEndEvent
from hother.streamblocks_examples.helpers.simulator import simple_text_stream
class TaskMetadata(BaseMetadata):
"""Custom metadata for task blocks."""
id: str
block_type: str
title: str = "Untitled"
priority: str = "normal"
class TaskContent(BaseContent):
"""Custom content for task blocks."""
description: str = ""
@classmethod
def parse(cls, raw_text: str) -> "TaskContent":
return cls(raw_content=raw_text, description=raw_text.strip())
TaskBlock = Block[TaskMetadata, TaskContent]
async def main() -> None:
"""Use a custom block type."""
registry = Registry(syntax=DelimiterFrontmatterSyntax())
registry.register("task", TaskBlock)
processor = StreamBlockProcessor(registry)
text = "!!start\n---\nid: task-1\nblock_type: task\ntitle: Fix bug\npriority: high\n---\nFix the login issue\n!!end"
stream = simple_text_stream(text)
async for event in processor.process_stream(stream):
if isinstance(event, BlockEndEvent):
block = event.get_block()
if block:
print("\nExtracted Task Block:")
print(block.model_dump_json(indent=2))
if __name__ == "__main__":
asyncio.run(main())