Syntaxes
A syntax defines the wire format of a block: how its opening is detected, where metadata lives, and what closes it. Each Registry holds exactly one syntax instance. StreamBlocks ships three built-in syntaxes and lets you implement your own.
Every block has the same anatomy regardless of syntax: an opening marker, an optional metadata section, the content, and a closing marker. Here it is for DelimiterFrontmatterSyntax:
DelimiterPreambleSyntax
The default. Metadata is inline in the opening line, compact and cheap for an LLM to emit.
The opening delimiter carries the block id and block_type (both required, alphanumeric).
# 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))
| Option | Default | Description |
|---|---|---|
delimiter |
"!!" |
Marker prefix for the opening line and the !!end closing line |
Extra parameters in the opening line
Optional colon-separated parameters are stored in metadata as param_0, param_1, and so on:
produces metadata {"id": "file123", "block_type": "operation", "param_0": "create", "param_1": "urgent"}.
DelimiterFrontmatterSyntax
Delimiter markers with a YAML frontmatter section for richer metadata. The YAML is parsed into your metadata model (it must include id and block_type when using BaseMetadata); nested YAML is supported.
# Create delimiter frontmatter syntax for tasks
# Using standard !!start/!!end delimiters
task_syntax = DelimiterFrontmatterSyntax(
start_delimiter="!!start",
end_delimiter="!!end",
)
# Create type-specific registry and register block
registry = Registry(syntax=task_syntax)
registry.register("task", TaskBlock)
| Option | Default | Description |
|---|---|---|
start_delimiter |
"!!start" |
Opening marker |
end_delimiter |
"!!end" |
Closing marker |
MarkdownFrontmatterSyntax
Markdown fenced code blocks with optional YAML frontmatter, useful when the stream is rendered as Markdown anyway. When frontmatter is absent (or has no block_type), the info string is the fallback block_type and all lines become content.
# Create markdown frontmatter syntax for patch blocks
# Each Registry holds exactly one syntax.
# To handle multiple info strings (patch/yaml/diff), you would need separate processors
# or a custom syntax that handles multiple patterns internally.
syntax = MarkdownFrontmatterSyntax(
fence="```",
info_string="patch", # Will match ```patch blocks
)
# Create type-specific registry and register block
registry = Registry(syntax=syntax)
registry.register("patch", Patch)
# Create processor with config
config = ProcessorConfig(lines_buffer=10)
processor = StreamBlockProcessor(registry, config=config)
| Option | Default | Description |
|---|---|---|
fence |
"```" |
Fence string |
info_string |
None |
Restricts detection to fences with this info string; also the fallback block_type |
How a syntax is chosen
Pass either a Syntax enum member or a configured instance to the registry:
from hother.streamblocks import Registry, DelimiterFrontmatterSyntax
from hother.streamblocks.syntaxes.factory import Syntax
# Enum: built-in syntax with default options
registry = Registry(syntax=Syntax.DELIMITER_FRONTMATTER)
# Instance: full control over options
registry = Registry(syntax=DelimiterFrontmatterSyntax(start_delimiter="<<begin", end_delimiter="<<end"))
| Enum member | Syntax class |
|---|---|
Syntax.DELIMITER_PREAMBLE (default) |
DelimiterPreambleSyntax |
Syntax.DELIMITER_FRONTMATTER |
DelimiterFrontmatterSyntax |
Syntax.MARKDOWN_FRONTMATTER |
MarkdownFrontmatterSyntax |
Because a registry holds a single syntax, processing one stream with several wire formats requires separate processors or a custom syntax that recognizes multiple patterns.
Custom syntaxes
Subclass BaseSyntax and implement four methods:
| Method | Role |
|---|---|
detect_line(line, candidate) |
Return a DetectionResult flagging opening, closing, or metadata-boundary lines; accumulate lines into the candidate |
should_accumulate_metadata(candidate) |
Whether the candidate is still in its metadata section |
extract_block_type(candidate) |
Pull the block_type string out of a candidate so the registry can find the block class |
parse_block(candidate, block_class) |
Build the final ParseResult with parsed metadata and content |
A custom syntax plugs into the registry like any built-in one:
syntax = XMLBlockSyntax()
registry = Registry(syntax=syntax)
registry.register("files_operations", FileOperations)
processor = StreamBlockProcessor(registry)
The full example implements an XML-comment wire format (<!-- block:type id="..." --> … <!-- /block -->); see the custom syntax example for the complete XMLBlockSyntax implementation.
Next steps
- Blocks & Registry: how block classes attach to a syntax.
- Events: what the processor emits while a block accumulates.
- Syntax examples: runnable examples for every built-in syntax.