feat(flow): support custom persistence key in @persist (#5649)

* feat(flow): add optional key param to @persist decorator

Allows users to specify which state attribute to use as the
persistence key instead of always defaulting to state.id.

Usage: @persist(key='conversation_id')

Falls back to state.id when key is not provided (no breaking change).
Raises ValueError if the specified key is missing or falsy on state.

* docs(flow): document @persist key parameter for custom persistence keys

* fix(flow): use explicit None check for persist key to avoid empty-string fallback

---------

Co-authored-by: iris-clawd <iris-clawd@anthropic.com>
Co-authored-by: iris-clawd <iris@crewai.com>
Co-authored-by: Lorenze Jay <63378463+lorenzejay@users.noreply.github.com>
This commit is contained in:
Lucas Gomide
2026-04-29 13:41:20 -03:00
committed by GitHub
parent e1b53f684a
commit e2deac5575
14 changed files with 350 additions and 13 deletions

View File

@@ -380,6 +380,33 @@ class AnotherFlow(Flow[dict]):
print("Method-level persisted runs:", self.state["runs"])
```
### Custom Persistence Key
By default, `@persist` uses the auto-generated `state.id` field as the persistence key. If your flow models its own identifier — for example a `conversation_id` shared across sessions — you can pass a `key` argument and `@persist` will use that attribute as the flow UUID instead:
```python
from crewai.flow.flow import Flow, listen, start
from crewai.flow.persistence import persist
from pydantic import BaseModel
class ConversationState(BaseModel):
conversation_id: str
turn: int = 0
@persist(key="conversation_id") # Use a custom field as the persistence key
class ConversationFlow(Flow[ConversationState]):
@start()
def begin(self):
self.state.turn += 1
print(f"Conversation {self.state.conversation_id} turn {self.state.turn}")
# Resuming the same conversation reloads its prior state by conversation_id
flow = ConversationFlow(conversation_id="user-42")
flow.kickoff()
```
The decorator reads the value at `state[key]` for dict states, or `getattr(state, key)` for Pydantic / object states. If the named attribute is missing or falsy at save time, `@persist` raises a `ValueError` such as `Flow state is missing required persistence key 'conversation_id'`. When `key` is omitted, the existing behavior is preserved and `state.id` is used.
### How It Works
1. **Unique State Identification**

View File

@@ -146,6 +146,15 @@ class ProductionFlow(Flow[AppState]):
# ...
```
By default `@persist` keys saved state by the auto-generated `state.id`. If your application already has a natural identifier — for example a `conversation_id` that ties multiple runs to the same user session — pass it as `key` and the decorator will use that attribute as the flow UUID. A `ValueError` is raised if the named attribute is missing or falsy at save time.
```python
@persist(key="conversation_id")
class ProductionFlow(Flow[AppState]):
# AppState must expose conversation_id; resuming a session reloads its prior state
...
```
## Summary
- **Start with a Flow.**