Context: Same script as companion bug #1273. After hitting type errors on result.value, I checked the type stubs and found ComputedModelOutputThunk[str] — S is bound to Instruction's output type, not to the format= argument. The type system gave no way to express the correct type, so I suppressed its complaint with cast(MyModel, result.value). Pyright was satisfied.
That cast is exactly what hid the silent failure in #1273. The type system didn't guide me toward the wrong pattern by accident — it had no way to express the right one. A developer with no prior Mellea experience has no signal that cast is dangerous here; from the type stubs it looks correct.
The gap: session.act(action, format=MyModel) returns ComputedModelOutputThunk[S] where S is the Component's type variable — for Instruction, that's str. The format= argument is invisible to the type system.
Proposed fix: BaseModelSubclass already exists in core/backend.py as a TypeVar(bound=BaseModel, default=BaseModel). Add overloads using it to thread format= into the return type:
@overload
def act(
self, action: Component[Any], *, format: type[BaseModelSubclass],
return_sampling_results: Literal[False] = False, ...
) -> ComputedModelOutputThunk[BaseModelSubclass]: ...
@overload
def act(
self, action: Component[S], *, format: None = None,
return_sampling_results: Literal[False] = False, ...
) -> ComputedModelOutputThunk[S]: ...
Scope:
act and aact in session.py — return ComputedModelOutputThunk
instruct and ainstruct in session.py — same shape
- The equivalent methods in
functional.py — note these return tuple[ComputedModelOutputThunk[BaseModelSubclass], Context] in the format= overload, not just the thunk
chat and query have different return shapes and should be scoped separately
Test plan: add a reveal_type assertion in a type-stub test or run pyright --verifytypes to confirm the overloads resolve correctly.
Compatibility: type-only change, no runtime behaviour changes. Existing cast(MyModel, result.value) calls will correctly be flagged as unnecessary by strict Pyright — that's the intended outcome, guiding callers to the fix.
Related: #1177 (type annotations broadly). Companion to bug #1273.
Context: Same script as companion bug #1273. After hitting type errors on
result.value, I checked the type stubs and foundComputedModelOutputThunk[str]—Sis bound toInstruction's output type, not to theformat=argument. The type system gave no way to express the correct type, so I suppressed its complaint withcast(MyModel, result.value). Pyright was satisfied.That
castis exactly what hid the silent failure in #1273. The type system didn't guide me toward the wrong pattern by accident — it had no way to express the right one. A developer with no prior Mellea experience has no signal thatcastis dangerous here; from the type stubs it looks correct.The gap:
session.act(action, format=MyModel)returnsComputedModelOutputThunk[S]whereSis theComponent's type variable — forInstruction, that'sstr. Theformat=argument is invisible to the type system.Proposed fix:
BaseModelSubclassalready exists incore/backend.pyas aTypeVar(bound=BaseModel, default=BaseModel). Add overloads using it to threadformat=into the return type:Scope:
actandaactinsession.py— returnComputedModelOutputThunkinstructandainstructinsession.py— same shapefunctional.py— note these returntuple[ComputedModelOutputThunk[BaseModelSubclass], Context]in theformat=overload, not just the thunkchatandqueryhave different return shapes and should be scoped separatelyTest plan: add a
reveal_typeassertion in a type-stub test or runpyright --verifytypesto confirm the overloads resolve correctly.Compatibility: type-only change, no runtime behaviour changes. Existing
cast(MyModel, result.value)calls will correctly be flagged as unnecessary by strict Pyright — that's the intended outcome, guiding callers to the fix.Related: #1177 (type annotations broadly). Companion to bug #1273.