Replies: 9 comments 10 replies
-
For more determinism guarantees, see experimental strict mode: https://github.com/marimo-team/marimo/releases/tag/0.6.20
Not really, because execution path is really the fallback.
Yep, that's the primary hashing check. Caching does the best it can by relying on the contents of the variables it uses, and only falls back on execution mode if it is unable to hash the variable contents. It is possible to have an unhashable datatype that mutates between executions- but then your notebook is not deterministic regardless of As long as the variable data is hashable, then cache should be deterministic. If execution path is utilized- then as a limitation of python it is not possible to guarantee determinism, since file state and hidden memory can accumulate in ways that marimo cannot detect. You can mitigate against this by using strict mode- which requires a deep copy between each cell execution, and also has some other scheduling guarantees. But this of course is flawed in terms of performance. There's potential for a COW (copy on write mode), that might be an intermediate here- but I think this might be a little tricky to properly implement.
|
Beta Was this translation helpful? Give feedback.
-
I do like your latter suggestion of tracking file contents. |
Beta Was this translation helpful? Give feedback.
-
Could you please point to the place in code that does this? Between these lines in Lines 357 to 386 in 1a1db27 And Lines 453 to 482 in 1a1db27 I don't see how what you are saying takes place. Moreover, |
Beta Was this translation helpful? Give feedback.
-
A remark here is that even if the notebook is knowingly non-deterministic, e.g., it involves LLM generations with non-zero temperature that are used as inputs in downstream cells, it's still good to definitely know at the marimo level (and figure-outable across sudden runtime shutdowns, restarts, etc.) whether these non-deterministic cells are consistent with each other, i.e., whether they "resulted (or could have resulted) from a blank-slate notebook run top-to-bottom, without using cache". |
Beta Was this translation helpful? Give feedback.
-
|
Beta Was this translation helpful? Give feedback.
-
Just a little further down: Line 399 in 1a1db27 You can refer to the unit tests for edge cases if you are concerned: https://github.com/marimo-team/marimo/blob/1a1db277543da001fe8f1b0c84b7a9632c4645bd/tests/_save/test_hash.py Re the proposed bug IIRC, execution_refs in collect_for_content_hash are for Excited if you can think of a non-deterministic case! We did spend a fair bit of time thinking through edge cases
The hash mechanism is denoted as a prefix to the hash (e.g.
That is a good observation, I believe that I'll read over the docs again, but they shouldn't sound like caching is totally bullet proof. I'm fairly certain we call out randomness, network-requests and file system concerns |
Beta Was this translation helpful? Give feedback.
-
How it's not a certainty that the
|
Beta Was this translation helpful? Give feedback.
-
Re your simple example: Cell 2 will not run unless Cell 1 has, so I am unsure of the issue. In normal marimo, Cell 2 can be run manually if Cell 1 has failed (note a cache mechanism failure results in rerun)- but this behavior can be disabled with the before mentioned strict mode. Your example can also be written in a way such that there is no issue: @dataclass
class NotContentAddressable:
text: str
@app.cell
def one():
with mo.persistent_cache("llm_call") as cache:
a = NotContentAddressable(text=llm.prompt("blah", temperature=1))
@app.cell
def two():
text = a.text
with mo.persistent_cache("llm_feed") as cache:
_ = llm.prompt("do something with " + text) # Will now be content hash vs execution hash since text is 'primitive'
assert cache.cache_type == "ContentAddressed" If your LLM is non-deterministic, then maybe consider utilizing a seed. Caching and non-determinism do not mix well. From your feature specs it seems like you would like something more like an archive with quick retrieval, which I am not certain falls under the current scope of the caching feature. Re robustness- I appreciate your concern, but I do think it's a little misplaced unless you can provide concrete examples of where the hashing mechanism fails barring cases with side effects. I'm moving this over to "discussion" since it seems that better documentation/ communication is required for caching- but I just made an issue for incorporating tracked files into cache (#3271). But feel free to keep the discussion going ! |
Beta Was this translation helpful? Give feedback.
-
As is, there's no forceful rerun and no persistent cache execution mode, so this case is a hypothetical- but I hear you. I think this is a good design consideration for #3054 I think caching and non-determinism do not mix well- and an easy cache invalidation is a reason for the required cache "naming" mechanism. I think the best way to implement your type of "forced rerun" would be changing a seed value, which would invalidate the cell and downstream values. Or, in the case where you cannot control the seed- you can add a slider and reference it in the cell (for now). This UI/ api could be improved specifically for cache invalidation (and in your case LLM prompts) and be deterministic. Reproducibility is a key focus of the caching mechanism, and a "forced" rerun is best done via cache miss |
Beta Was this translation helpful? Give feedback.
-
Hashing strategy for "execution path" refs could lead to inconsistent notebook state
If automatic cell re-run is off, and an upstream cell is non-deterministic, then upon re-running the upstream cell the notebook is in inconsistent state and if at this moment the notebook is closed, then upon restart there would be no way to know for Marimo runtime that the cache result for the downstream cell are invalid.
Although
BlockHasher
's doc mentions this (albeit this isn't even a part of user-facing docs!), the phrase "sources of non-determinism are not accounted for in this implementation, and are left to the user" is not helpful. The core of Marimo's caching logic is exactly the place to deal with this, at least in a good fraction of cases.If the upstream cell is cached persistently (most likely because all cells are cached persistently, i.e., via #3054), then cached content hashing is vital for correctness and corruption prevention (see #3176). Then, if the upstream cell has been run in the current marimo notebook runtime (either forcefully, or because its own dependencies are invalidated), its results (i.e., "content") have also been serialised and cached, and thus the content hash for these latest results has also been recorded.
This means that we can include upstream execution path's content hash along with its module hash in the calculation of the block hash cheaply and at the same handle non-determinism much better.
File inputs
For even better handling of non-determinism, "file inputs" to cells should be tracked and also being part of the block hash (#3258). Thus, if the non-deterministic cell writes out different contents to these non-python files but has the same "content", the downstream cells would still record a cache miss and re-run.
Beta Was this translation helpful? Give feedback.
All reactions