4. Dependencies#
A field typed as RegistrationKey is a dependency. cinnamon records it as an edge
in the dependency graph, so it knows what a pipeline is made of before anything is
built.
python examples/tutorial/04_dependencies.py
class PipelineConfig(Configuration):
tokenizer: RegistrationKey = Param(
RegistrationKey(name="tokenizer", namespace=NAMESPACE),
description="Which tokenizer this pipeline uses",
)
max_tokens: int = Param(4)
Note what the component receives: the key itself, not a built object. The component decides when — and whether — to build its child.
class Pipeline:
def __init__(self, tokenizer: RegistrationKey, max_tokens: int):
# The child is built here, by the component, from the key it was given.
self.tokenizer = Registry.from_key(tokenizer)
self.max_tokens = max_tokens
def run(self, text: str) -> list[str]:
return self.tokenizer.tokenize(text)[: self.max_tokens]
That laziness is deliberate, and it is also why a dependency can be swapped without touching the parent’s code.
What to notice#
Sweeps compose down the graph.
TokenizerConfigdeclares a variant onlowercase; the pipeline never mentions it, and yet resolution produces one pipeline configuration per tokenizer variant. The parent’s key records which child it was built against, as a derived tag such astokenizer.lowercase=False.Nobody joined those up. The edge in the graph is enough.
Because the child arrives as a key, a component can hold a dependency it never builds — a branch that only some configurations take.
The whole file#
1"""
24. Dependencies -- configurations that reference other registrations.
3
4 python examples/tutorial/04_dependencies.py
5
6A field typed as ``RegistrationKey`` is a dependency. cinnamon records it as an
7edge in the dependency graph, so it knows what a pipeline is made of before
8anything is built.
9
10Note what the *component* receives: the key itself, not a built object. The
11component decides when -- and whether -- to build its child. That laziness is
12deliberate; it is also why a dependency can be swapped without touching the
13parent's code.
14"""
15
16from cinnamon.configuration import Configuration, Param
17from cinnamon.registry import RegistrationKey, Registry
18
19NAMESPACE = "tutorial"
20
21
22class Tokenizer:
23 def __init__(self, lowercase: bool):
24 self.lowercase = lowercase
25
26 def tokenize(self, text: str) -> list[str]:
27 return (text.lower() if self.lowercase else text).split()
28
29
30class Pipeline:
31 def __init__(self, tokenizer: RegistrationKey, max_tokens: int):
32 # The child is built here, by the component, from the key it was given.
33 self.tokenizer = Registry.from_key(tokenizer)
34 self.max_tokens = max_tokens
35
36 def run(self, text: str) -> list[str]:
37 return self.tokenizer.tokenize(text)[: self.max_tokens]
38
39
40class TokenizerConfig(Configuration):
41 # A variant here will show up on the *pipeline* too, further down.
42 lowercase: bool = Param(True, variants=[False])
43
44
45class PipelineConfig(Configuration):
46 tokenizer: RegistrationKey = Param(
47 RegistrationKey(name="tokenizer", namespace=NAMESPACE),
48 description="Which tokenizer this pipeline uses",
49 )
50 max_tokens: int = Param(4)
51
52
53def main() -> None:
54 Registry.initialize()
55
56 Registry.register_configuration(
57 TokenizerConfig(),
58 name="tokenizer",
59 namespace=NAMESPACE,
60 component=f"{__name__}.Tokenizer",
61 )
62 Registry.register_configuration(
63 PipelineConfig(),
64 name="pipeline",
65 namespace=NAMESPACE,
66 component=f"{__name__}.Pipeline",
67 )
68
69 valid_keys, _ = Registry.dag_resolution()
70
71 pipeline_key = RegistrationKey(name="pipeline", namespace=NAMESPACE)
72
73 # `dependencies` reports the fields cinnamon treats as graph edges.
74 config = Registry.retrieve_configuration(registration_key=pipeline_key)
75 print("pipeline depends on:")
76 for field_name, dependency in config.dependencies.items():
77 print(f" {field_name} -> {dependency}")
78
79 pipeline = Registry.from_key(pipeline_key)
80 print("\nresult:", pipeline.run("The Quick Brown Fox Jumps Over"))
81
82 # The tokenizer's own variant propagates: the pipeline gains a key per
83 # child variant, tagged with which child it was built against. Sweeps
84 # compose down the graph without anyone wiring them together.
85 print("\nevery pipeline configuration resolution produced:")
86 for key in sorted((k for k in valid_keys if k.name == "pipeline"), key=str):
87 built = Registry.from_key(key)
88 tags = ", ".join(sorted(key.tags)) or "(defaults)"
89 print(f" {tags:28s} lowercase={built.tokenizer.lowercase}")
90
91
92if __name__ == "__main__":
93 main()
Next: 5. Collections — depending on many registrations at once.