Skip to main content
Lola Send’s application layer holds no persistent data. All conversation state and message history live in Redis, and sender identity lives in backend services. This stateless design enables horizontal scaling — any application instance can handle any request without session affinity.

Two stores, two purposes

Lola Send uses Redis for two distinct concerns, each with its own store and configuration:
StoreClass chainPurpose
Conversation stateRedisChatStateStoreRedisChatStateProviderKey-value session data per conversation (e.g., current step in a flow, working context)
Conversation historyRedisHistoryStoreRedisHistoryProviderAsyncListRedisStoreAsyncOrdered message history per session, providing the context window for the AI model

Conversation state

The state store holds working context for each active conversation — such as which step the sender is on in a multi-step flow, temporary data collected during the conversation, and agent-specific flags. Agents read and write state through a context manager:
async with ctx.state_manager() as state:
    count = state.get("count", 0)
    count += 1
    state["count"] = count

Conversation history

The history store maintains an ordered record of messages exchanged in each session. The AI model uses this history as its context window — core_history_window_length in MacawSettings controls how many turns the model sees. History can be cleared programmatically (e.g., when a session resets via the /new command).

Stateless application design

The application itself is stateless:
  • Session state lives in Redis, not in application memory
  • Sender identity lives in the backend identity service, injected per-request by middleware
  • Conversation history lives in Redis, accessed asynchronously
  • Configuration comes from environment variables (managed by Doppler)
Any application instance can handle any inbound message. There is no session affinity, no in-memory cache of sender data, and no instance-specific state. This enables:
  • Horizontal scaling — add instances to handle load without coordination
  • Zero-downtime deployments — replace instances without losing state
  • Fault tolerance — if an instance fails, other instances continue processing

Redis configuration

State and history use separate Redis configurations for isolation:
StoreEnvironment variablePurpose
Conversation stateSTATE_STORE_REDISConnection URL for the state Redis instance
Conversation historyHISTORY_STORE_REDISConnection URL for the history Redis instance
Separate configurations allow the bank to:
  • Deploy state and history to different Redis instances for performance isolation
  • Apply different persistence, eviction, and backup policies per store
  • Monitor and scale each store independently

Session scoping

Redis keys are scoped by session ID, which is derived from the sender’s phone number and channel. This ensures:
  • Conversations from different senders are isolated
  • The same sender on different channels has separate sessions
  • No cross-contamination of state between conversations

Singleton pattern

Both stores are implemented as singletons — created once at application startup and shared across all agents:
def get_state_store():
    state_store = RedisChatStateStore()
    return state_store.get_store()

def get_history_store():
    history_store = RedisHistoryStore()
    return history_store.get_store()
Every agent receives the same store instances, ensuring consistent state access regardless of which agent handles a given message.

Security considerations

  • No sensitive data in state: Card numbers, full PII, and other sensitive data are never persisted in conversation state or history. Sensitive operations are handled through external encrypted widgets.
  • VPC deployment: Redis instances deploy within the virtual private cloud, not exposed to the public internet.
  • Ephemeral data: Conversation state has a natural lifecycle tied to the session. State does not accumulate indefinitely.
  • No credentials in state: Authentication tokens and secrets are managed by Doppler, not stored in Redis.
Conversation state and history should be treated as ephemeral. Do not rely on Redis state for persistent business records — use backend services for durable storage.

Configuration and control

ControlDescription
Redis instancesSeparate connection URLs for state and history stores
History windowcore_history_window_length in MacawSettings controls how many turns the AI model sees
State lifecycleSessions can be cleared programmatically (e.g., on identity reset)
IsolationSeparate Redis instances enable independent scaling and monitoring