Summary
All 5 modality Parameters TypedDict subclasses — TextParameters, ImageParameters, VideoParameters, AudioParameters, EmbeddingsParameters — declare their fields without total=False. Under strict mypy, every field on these classes is treated as required, which cascades through Unpack[...] (PEP 692) to break every caller of celeste.text.generate, celeste.images.generate, etc.
The templates/modalities/{modality_slug}/parameters.py.template has the same bug, so every new modality scaffolded from it inherits the issue.
Root cause
Per PEP 589:
"The totality flag only applies to items defined in the body of the TypedDict definition. Inherited items won't be affected, and instead use totality of the TypedDict type where they were defined."
The base class is correct:
# src/celeste/parameters.py:10
class Parameters(TypedDict, total=False):
"""Base parameters for all capabilities."""
But every subclass declares its own fields without carrying the flag over:
# src/celeste/modalities/text/parameters.py:44
class TextParameters(Parameters): # ← missing total=False
temperature: float
max_tokens: int
seed: int
thinking_budget: int | str
thinking_level: str
output_schema: type[BaseModel]
tools: list[ToolDefinition]
tool_choice: ToolChoiceOption
verbosity: str
web_search: bool
x_search: bool
code_execution: bool
Because TextParameters itself does not declare total=False, every field in its own body is required from mypy's perspective.
Why it hasn't surfaced internally
pyproject.toml (lines 167-185) has [[tool.mypy.overrides]] blocks that blanket-disable call-arg on celeste.modalities.*.client, celeste.providers.*.*, and other internal modules. Those overrides silence the error at the internal call sites but do not protect downstream consumers.
How it surfaces downstream
A strict-mypy downstream package (celeste-tools, which uses celeste.text.generate with strict type-checking enabled) hits 37 [call-arg] errors like:
src/celeste_tools/web_fetch/web_fetch.py:182: error: Missing named argument "temperature" for "generate" of "TextNamespace" [call-arg]
src/celeste_tools/web_fetch/web_fetch.py:182: error: Missing named argument "max_tokens" for "generate" of "TextNamespace" [call-arg]
src/celeste_tools/web_fetch/web_fetch.py:182: error: Missing named argument "seed" for "generate" of "TextNamespace" [call-arg]
...
src/celeste_tools/web_fetch/web_fetch.py:182: error: Missing named argument "code_execution" for "generate" of "TextNamespace" [call-arg]
One error per TextParameters field, per call site.
Affected files
| File |
Line |
Class |
src/celeste/modalities/text/parameters.py |
44 |
TextParameters |
src/celeste/modalities/images/parameters.py |
29 |
ImageParameters |
src/celeste/modalities/videos/parameters.py |
20 |
VideoParameters |
src/celeste/modalities/audio/parameters.py |
18 |
AudioParameters |
src/celeste/modalities/embeddings/parameters.py |
17 |
EmbeddingsParameters |
templates/modalities/{modality_slug}/parameters.py.template |
35 |
{Modality}Parameters |
Fix
Six one-word changes. Add , total=False to each class declaration:
class TextParameters(Parameters, total=False):
class ImageParameters(Parameters, total=False):
class VideoParameters(Parameters, total=False):
class AudioParameters(Parameters, total=False):
class EmbeddingsParameters(Parameters, total=False):
# And in the template
class {Modality}Parameters(Parameters, total=False):
Why total=False and not PEP 655 NotRequired[...]
- PEP 655 explicitly states the two forms are equivalent and does not deprecate
total=False.
- The
Parameters base is already total=False — keeping the subclasses consistent.
- Ecosystem precedent for all-optional TypedDicts used with
Unpack[...]: pydantic.ConfigDict, openai.types.chat.CompletionCreateParamsBase, anthropic.types.MessageCreateParamsBase, google-genai param types, and typeshed stubs all use TypedDict, total=False with occasional Required[...] for the rare genuinely-required fields. Zero NotRequired hits in openai-python's and anthropic-sdk-python's types folders.
Follow-up (not this issue)
Once the root cause is fixed, several [[tool.mypy.overrides]] blocks in pyproject.toml that disable call-arg on celeste.modalities.*.client may no longer be needed. Worth auditing in a separate PR.
Summary
All 5 modality
ParametersTypedDict subclasses —TextParameters,ImageParameters,VideoParameters,AudioParameters,EmbeddingsParameters— declare their fields withouttotal=False. Under strict mypy, every field on these classes is treated as required, which cascades throughUnpack[...](PEP 692) to break every caller ofceleste.text.generate,celeste.images.generate, etc.The
templates/modalities/{modality_slug}/parameters.py.templatehas the same bug, so every new modality scaffolded from it inherits the issue.Root cause
Per PEP 589:
The base class is correct:
But every subclass declares its own fields without carrying the flag over:
Because
TextParametersitself does not declaretotal=False, every field in its own body is required from mypy's perspective.Why it hasn't surfaced internally
pyproject.toml(lines 167-185) has[[tool.mypy.overrides]]blocks that blanket-disablecall-argonceleste.modalities.*.client,celeste.providers.*.*, and other internal modules. Those overrides silence the error at the internal call sites but do not protect downstream consumers.How it surfaces downstream
A strict-mypy downstream package (
celeste-tools, which usesceleste.text.generatewith strict type-checking enabled) hits 37[call-arg]errors like:One error per
TextParametersfield, per call site.Affected files
src/celeste/modalities/text/parameters.pyTextParameterssrc/celeste/modalities/images/parameters.pyImageParameterssrc/celeste/modalities/videos/parameters.pyVideoParameterssrc/celeste/modalities/audio/parameters.pyAudioParameterssrc/celeste/modalities/embeddings/parameters.pyEmbeddingsParameterstemplates/modalities/{modality_slug}/parameters.py.template{Modality}ParametersFix
Six one-word changes. Add
, total=Falseto each class declaration:Why
total=Falseand not PEP 655NotRequired[...]total=False.Parametersbase is alreadytotal=False— keeping the subclasses consistent.Unpack[...]:pydantic.ConfigDict,openai.types.chat.CompletionCreateParamsBase,anthropic.types.MessageCreateParamsBase,google-genaiparam types, and typeshed stubs all useTypedDict, total=Falsewith occasionalRequired[...]for the rare genuinely-required fields. ZeroNotRequiredhits in openai-python's and anthropic-sdk-python's types folders.Follow-up (not this issue)
Once the root cause is fixed, several
[[tool.mypy.overrides]]blocks inpyproject.tomlthat disablecall-argonceleste.modalities.*.clientmay no longer be needed. Worth auditing in a separate PR.