Repository navigation
Commit 736c349
Fingerprint durable agent steps that hold sets or values that are not JSON (#71575)
Plain JSON tool arguments and settings hash exactly as before, and the
message history and request parameters are still hashed from pydantic's
json-mode dump, so stored fingerprints keep matching. Other values go
through to_jsonable_python, so datetime, Decimal, dataclasses, bytes and
non-string dict keys fingerprint instead of coming back as None.
The json rendering is paired with pydantic's python-mode dump of the same
payload, which has the same shape but keeps sets as sets and wraps an
iterator instead of reading it. A list is sorted where the dump holds a
set, since a set of strings otherwise hashes differently in every process.
Dict keys that collide once rendered, and anything that renders through an
iterator, are refused rather than hashed. Tool arguments are rendered from
deep copies, so fingerprinting cannot change what the tool receives.
Whatever rendering raises costs the step a live re-run, never the task,
and both warnings name the step, the error and its type.
* Do not replay or cache AI steps that cannot be verified
A request that could not be fingerprinted came back as None, and None
equalled the None stored with the entry, so the replay guard passed for
exactly the requests it could not verify. The guard now requires a real
fingerprint, and such a step is no longer written to the cache, since an
entry without a fingerprint could never be replayed. It counts as skipped,
so the end-of-run warning names it, and a usable entry from an earlier
attempt survives instead of being overwritten.
* Document what the durable fingerprint covers and refuses
Says which values fingerprint, what is refused and why, what changes for
entries cached by an earlier version, and when a step that cannot be
fingerprinted costs the rest of the run.
* Sort a set only where its rendering is found
A list was sorted wherever the python-mode dump had a set at the same
place, which a serializer that applies only in JSON mode could defeat by
putting another list there. A list is now sorted only where it holds the
members of that set, rendered on their own. A set that cannot be found is
refused when its order follows the hash seed, and kept as rendered when it
does not, such as a set of integers.
Non-string dict keys are rendered on their own, so a serializer that drops
or reorders entries is no longer mistaken for colliding keys, or paired
with a neighbouring value.
A set of models or dataclass instances, or a dict keyed by one, has no
python-mode dump. The message history and request parameters then hash
from the JSON rendering alone, as they did before.
The hash-seed tests share one snippet run under two seeds.
* Order sets holding NaN and refuse bytes keys that collide in a model
A NaN hashes by its identity, so a set holding one iterated differently in
every run, and the message dump renders it as null where the set's own
rendering kept it, so the set was never found and was hashed in that order.
The set's own rendering now also tries the null mode, and a NaN no longer
counts as seed-free.
Inside a model, b"a" and "a" render as one key, which rendering the keys on
their own did not show, so two different payloads could share a digest. A
dict with a bytes key that renders fewer keys is now refused, as one with a
nan or inf key already was.
The docs now say that a set behind a JSON-only serializer that renames or
reshapes it is refused even when that serializer sorts it, that a dict keyed
by a model is only refused from pydantic 2.13, and that a value python mode
cannot dump turns off set ordering for the whole history.
Tests pin the template branches for enum members, tuples holding sets, dict
members and the empty set, and add those set shapes to the cross-seed run.
The two pairs of caching tests that differed only in whether an entry
already existed are now one parametrized test each.
---------
Co-authored-by: Kaxil Naik <kaxilnaik@gmail.com>1 parent e1d881b commit 736c349
4 files changed
Lines changed: 2173 additions & 35 deletions
File tree
- providers/common/ai
- docs
- src/airflow/providers/common/ai/durable
- tests/unit/common/ai/durable
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
193 | 193 | | |
194 | 194 | | |
195 | 195 | | |
| 196 | + | |
| 197 | + | |
| 198 | + | |
| 199 | + | |
| 200 | + | |
| 201 | + | |
| 202 | + | |
| 203 | + | |
| 204 | + | |
| 205 | + | |
| 206 | + | |
| 207 | + | |
| 208 | + | |
| 209 | + | |
| 210 | + | |
| 211 | + | |
| 212 | + | |
| 213 | + | |
| 214 | + | |
| 215 | + | |
| 216 | + | |
| 217 | + | |
| 218 | + | |
| 219 | + | |
| 220 | + | |
| 221 | + | |
| 222 | + | |
| 223 | + | |
| 224 | + | |
| 225 | + | |
| 226 | + | |
| 227 | + | |
| 228 | + | |
| 229 | + | |
| 230 | + | |
| 231 | + | |
| 232 | + | |
| 233 | + | |
| 234 | + | |
| 235 | + | |
| 236 | + | |
| 237 | + | |
| 238 | + | |
| 239 | + | |
| 240 | + | |
| 241 | + | |
| 242 | + | |
| 243 | + | |
| 244 | + | |
196 | 245 | | |
197 | 246 | | |
198 | 247 | | |
| |||
0 commit comments