2525only when those held-out scores beat the current setting's. This discourages gains confined to
2626small portions of the dataset, but can accept them when they recur across folds.
2727
28- On a generative LM, a native `bool` or `Literal` output returns its value without probabilities
29- unless it has an entry in `fields`. Calibration adds that entry, which asks the LM for
30- probabilities, and keeps it only when the fitted setting beats the native output under the same
31- fold check .
28+ Calibration enables probability-based execution for every compatible output by adding an entry
29+ in `fields`. After fitting, it compares against the original behavior under the same fold check
30+ and restores the original field configuration unless the fitted behavior wins. The adapter
31+ handles probability requests for the backend used on each call .
3232
3333"""
3434
@@ -61,34 +61,23 @@ def predictors(program) -> list[tuple[str, Predict]]:
6161 return [(name , p ) for name , p in program .named_parameters () if isinstance (p , Predict ) and decision_outputs (p )]
6262
6363
64- def resolved_lm (predict : Predict ):
65- """The client the predictor calls: its own, or the configured one ."""
64+ def caches (predict : Predict ) -> bool :
65+ """Check caching for a statically bound or configured client ."""
6666 lm = predict .lm or dspy .settings .lm
6767 if lm is None :
68- raise ValueError ("ReAnchor needs an LM or decision client, bound to the predictor or configured globally." )
69- return lm
70-
71-
72- def caches (predict : Predict ) -> bool :
73- """Whether repeated requests from the predictor are answered from the cache."""
74- lm = resolved_lm (predict )
68+ raise ValueError (
69+ "ReAnchor cannot check caching without a bound or globally configured client. "
70+ "For runtime client selection, pass require_cache=False."
71+ )
7572 enabled = predict .config .get ("cache" , getattr (lm , "cache" , False ))
7673 return bool (enabled ) and getattr (lm , "_cache_responses" , True )
7774
7875
79- def _state (predict : Predict , fields : dict ) -> DecisionState :
80- system_one = getattr (resolved_lm (predict ), "supports_decision_requests" , False ) is True
81- return DecisionState (predict .signature , fields , system_one = system_one )
82-
83-
84- def evidenced (predict : Predict ) -> set [str ]:
85- """The outputs the predictor currently decodes from probabilities."""
86- return set (_state (predict , predict .fields ).types )
87-
88-
8976def effective (predict : Predict , field : str ) -> dict :
9077 """The output's full configuration: its stored overrides on top of its type's defaults."""
91- return _state (predict , {** predict .fields , field : predict .fields .get (field , {})}).fields [field ]
78+ fields = dict (predict .fields )
79+ fields .setdefault (field , {})
80+ return DecisionState (predict .signature , fields ).fields [field ]
9281
9382
9483def metric_value (metric : Callable , example , pred ) -> float :
@@ -138,8 +127,7 @@ def score() -> list[float]:
138127 for name , predict in predictors (program ):
139128 for field , kind in decision_outputs (predict ).items ():
140129 original = copy .deepcopy (predict .fields .get (field ))
141- promoted = field not in evidenced (predict )
142- native = score () if promoted else None
130+ before = score ()
143131 predict .fields [field ] = effective (predict , field )
144132 evidence = _observe (program , predict , field , trainset , metric , num_threads )
145133 row = {"predictor" : name , "field" : field }
@@ -153,20 +141,11 @@ def score() -> list[float]:
153141 base = score ()
154142 best , fitted , observed , check = fit (predict , field , kind , evidence , score , base )
155143 row .update (parameter = parameter , value = best , train_score = _mean (fitted ), train_score_at_start = _mean (base ))
156- row .update (observed = observed , fold_check = check )
157- if promoted :
158- row ["train_score_native" ] = _mean (native )
159- if _select (native , [(fitted , (), None )])[0 ] is None :
160- _restore (predict , field , original )
161- row = {
162- "predictor" : name ,
163- "field" : field ,
164- "skipped" : "probabilities did not beat the native output" ,
165- "train_score_native" : row ["train_score_native" ],
166- "train_score" : row ["train_score" ],
167- }
168- else :
169- row ["promoted" ] = True
144+ row .update (observed = observed , fold_check = check , train_score_original = _mean (before ))
145+ if _select (before , [(fitted , (), None )])[0 ] is None :
146+ _restore (predict , field , original )
147+ row .pop ("value" )
148+ row .update (skipped = "fitted behavior did not beat the original" , train_score = _mean (before ))
170149 report .append (row )
171150 return report
172151
@@ -211,7 +190,8 @@ def _gaps(values: list[float], lo: float, hi: float, geometric: bool = False) ->
211190 gaps = []
212191 for a , b in itertools .pairwise (points ):
213192 middle , width = (math .sqrt (a * b ), math .log (b / a )) if geometric else ((a + b ) / 2 , b - a )
214- gaps .append ((_tidy (middle , a , b ), width ))
193+ if a < middle < b : # Adjacent floats can round the midpoint onto a boundary.
194+ gaps .append ((_tidy (middle , a , b ), width ))
215195 return gaps
216196
217197
0 commit comments