or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

Files

docs

activity-handling.mdbot-adapters.mdindex.mdmessage-factories.mdmiddleware.mdoauth-authentication.mdstate-management.mdstorage.mdtelemetry-logging.mdtesting-utilities.mdturn-context.md

state-management.mddocs/

0

# State Management

1

2

Persistent state management across conversation turns with support for different scopes (user, conversation, private conversation) and custom storage implementations. Includes state property accessors and automatic state persistence.

3

4

## Capabilities

5

6

### BotState Base Class

7

8

Abstract base class that provides the foundation for all state management in the Bot Framework, implementing property management, loading, and saving state data with storage backend integration.

9

10

```python { .api }

11

class BotState:

12

def __init__(self, storage, context_service_key: str):

13

"""

14

Initialize bot state.

15

16

Args:

17

storage (Storage): Storage implementation

18

context_service_key (str): Key for storing state in turn context

19

"""

20

21

def create_property(self, name: str):

22

"""

23

Create a state property accessor.

24

25

Args:

26

name (str): Name of the property

27

28

Returns:

29

StatePropertyAccessor: Property accessor

30

"""

31

32

async def load(self, turn_context: TurnContext, force: bool = False):

33

"""

34

Load state from storage.

35

36

Args:

37

turn_context (TurnContext): Current turn context

38

force (bool): Force reload even if already loaded

39

"""

40

41

async def save_changes(self, turn_context: TurnContext, force: bool = False):

42

"""

43

Save state changes to storage.

44

45

Args:

46

turn_context (TurnContext): Current turn context

47

force (bool): Force save even if no changes detected

48

"""

49

50

async def clear_state(self, turn_context: TurnContext):

51

"""

52

Clear current state.

53

54

Args:

55

turn_context (TurnContext): Current turn context

56

"""

57

58

def get_storage_key(self, turn_context: TurnContext):

59

"""

60

Get storage key for this state. Must be implemented by derived classes.

61

62

Args:

63

turn_context (TurnContext): Current turn context

64

65

Returns:

66

str: Storage key

67

"""

68

69

async def delete(self, turn_context: TurnContext):

70

"""

71

Delete state from storage.

72

73

Args:

74

turn_context (TurnContext): Current turn context

75

"""

76

```

77

78

### ConversationState

79

80

Manages conversation-scoped state that persists across turns within a single conversation. Data is shared among all participants in the conversation.

81

82

```python { .api }

83

class ConversationState(BotState):

84

def __init__(self, storage):

85

"""

86

Initialize conversation state.

87

88

Args:

89

storage (Storage): Storage implementation for persistence

90

"""

91

92

def get_storage_key(self, turn_context: TurnContext):

93

"""

94

Get storage key based on conversation ID.

95

96

Args:

97

turn_context (TurnContext): Current turn context

98

99

Returns:

100

str: Conversation-scoped storage key

101

"""

102

```

103

104

### UserState

105

106

Manages user-scoped state that persists across conversations for a specific user. Data follows the user regardless of which conversation they're in.

107

108

```python { .api }

109

class UserState(BotState):

110

def __init__(self, storage):

111

"""

112

Initialize user state.

113

114

Args:

115

storage (Storage): Storage implementation for persistence

116

"""

117

118

def get_storage_key(self, turn_context: TurnContext):

119

"""

120

Get storage key based on user ID.

121

122

Args:

123

turn_context (TurnContext): Current turn context

124

125

Returns:

126

str: User-scoped storage key

127

"""

128

```

129

130

### PrivateConversationState

131

132

Manages private conversation state that is scoped to both user and conversation. Provides user-specific state within a particular conversation context.

133

134

```python { .api }

135

class PrivateConversationState(BotState):

136

def __init__(self, storage):

137

"""

138

Initialize private conversation state.

139

140

Args:

141

storage (Storage): Storage implementation for persistence

142

"""

143

144

def get_storage_key(self, turn_context: TurnContext):

145

"""

146

Get storage key based on user and conversation ID.

147

148

Args:

149

turn_context (TurnContext): Current turn context

150

151

Returns:

152

str: Private conversation-scoped storage key

153

"""

154

```

155

156

### StatePropertyAccessor

157

158

Provides type-safe access to individual properties within bot state, handling default values, property getting/setting, and deletion.

159

160

```python { .api }

161

class StatePropertyAccessor:

162

def __init__(self, state: BotState, name: str):

163

"""

164

Initialize property accessor.

165

166

Args:

167

state (BotState): Parent state object

168

name (str): Property name

169

"""

170

171

async def get(self, turn_context: TurnContext, default_value_or_factory=None):

172

"""

173

Get property value.

174

175

Args:

176

turn_context (TurnContext): Current turn context

177

default_value_or_factory: Default value or factory function

178

179

Returns:

180

object: Property value or default

181

"""

182

183

async def set(self, turn_context: TurnContext, value):

184

"""

185

Set property value.

186

187

Args:

188

turn_context (TurnContext): Current turn context

189

value: Value to set

190

"""

191

192

async def delete(self, turn_context: TurnContext):

193

"""

194

Delete property.

195

196

Args:

197

turn_context (TurnContext): Current turn context

198

"""

199

```

200

201

### BotStateSet

202

203

Collection of bot states that can be managed together, providing convenient methods for loading and saving multiple state objects.

204

205

```python { .api }

206

class BotStateSet:

207

def __init__(self, *bot_states):

208

"""

209

Initialize bot state set.

210

211

Args:

212

*bot_states: Variable number of BotState objects

213

"""

214

215

def add(self, bot_state: BotState):

216

"""

217

Add a bot state to the set.

218

219

Args:

220

bot_state (BotState): State to add

221

"""

222

223

async def load_all(self, turn_context: TurnContext, force: bool = False):

224

"""

225

Load all states in the set.

226

227

Args:

228

turn_context (TurnContext): Current turn context

229

force (bool): Force reload even if already loaded

230

"""

231

232

async def save_all_changes(self, turn_context: TurnContext, force: bool = False):

233

"""

234

Save changes for all states in the set.

235

236

Args:

237

turn_context (TurnContext): Current turn context

238

force (bool): Force save even if no changes detected

239

"""

240

```

241

242

### StatePropertyInfo

243

244

Provides information about state properties for introspection and debugging purposes.

245

246

```python { .api }

247

class StatePropertyInfo:

248

def __init__(self, name: str, type_name: str = None):

249

"""

250

Initialize property info.

251

252

Args:

253

name (str): Property name

254

type_name (str, optional): Type name for the property

255

"""

256

self.name = name

257

self.type_name = type_name

258

```

259

260

## Usage Examples

261

262

### Basic State Setup

263

264

```python

265

from botbuilder.core import (

266

ActivityHandler, TurnContext, ConversationState,

267

UserState, MemoryStorage, MessageFactory

268

)

269

270

class StateBot(ActivityHandler):

271

def __init__(self):

272

# Create storage

273

memory_storage = MemoryStorage()

274

275

# Create state objects

276

self.conversation_state = ConversationState(memory_storage)

277

self.user_state = UserState(memory_storage)

278

279

# Create property accessors

280

self.user_profile_accessor = self.user_state.create_property("UserProfile")

281

self.conversation_data_accessor = self.conversation_state.create_property("ConversationData")

282

283

async def on_message_activity(self, turn_context: TurnContext):

284

# Get user profile

285

user_profile = await self.user_profile_accessor.get(

286

turn_context,

287

lambda: {"name": None, "message_count": 0}

288

)

289

290

# Get conversation data

291

conversation_data = await self.conversation_data_accessor.get(

292

turn_context,

293

lambda: {"turn_count": 0}

294

)

295

296

# Update data

297

user_profile["message_count"] += 1

298

conversation_data["turn_count"] += 1

299

300

# Save changes

301

await self.conversation_state.save_changes(turn_context)

302

await self.user_state.save_changes(turn_context)

303

304

# Send response

305

reply = f"User messages: {user_profile['message_count']}, Turn: {conversation_data['turn_count']}"

306

await turn_context.send_activity(MessageFactory.text(reply))

307

```

308

309

### Complex State Management

310

311

```python

312

class UserProfile:

313

def __init__(self):

314

self.name = None

315

self.age = None

316

self.preferences = {}

317

self.last_activity = None

318

319

class ConversationData:

320

def __init__(self):

321

self.topic = None

322

self.turn_count = 0

323

self.participants = []

324

325

class AdvancedStateBot(ActivityHandler):

326

def __init__(self):

327

memory_storage = MemoryStorage()

328

329

self.conversation_state = ConversationState(memory_storage)

330

self.user_state = UserState(memory_storage)

331

self.private_conversation_state = PrivateConversationState(memory_storage)

332

333

# Create typed property accessors

334

self.user_profile_accessor = self.user_state.create_property("UserProfile")

335

self.conversation_data_accessor = self.conversation_state.create_property("ConversationData")

336

self.private_data_accessor = self.private_conversation_state.create_property("PrivateData")

337

338

async def on_message_activity(self, turn_context: TurnContext):

339

# Get state data with default objects

340

user_profile = await self.user_profile_accessor.get(turn_context, UserProfile)

341

conversation_data = await self.conversation_data_accessor.get(turn_context, ConversationData)

342

private_data = await self.private_data_accessor.get(turn_context, lambda: {"notes": []})

343

344

# Update state based on message

345

text = turn_context.activity.text.lower()

346

347

if text.startswith("my name is"):

348

user_profile.name = text[11:].strip()

349

await turn_context.send_activity(MessageFactory.text(f"Nice to meet you, {user_profile.name}!"))

350

351

elif text.startswith("topic:"):

352

conversation_data.topic = text[6:].strip()

353

await turn_context.send_activity(MessageFactory.text(f"Changed topic to: {conversation_data.topic}"))

354

355

else:

356

# Add private note

357

private_data["notes"].append(f"Turn {conversation_data.turn_count}: {text}")

358

359

# Update turn count

360

conversation_data.turn_count += 1

361

362

# Save all changes

363

await self.user_state.save_changes(turn_context)

364

await self.conversation_state.save_changes(turn_context)

365

await self.private_conversation_state.save_changes(turn_context)

366

```

367

368

### Using BotStateSet

369

370

```python

371

class StateSetBot(ActivityHandler):

372

def __init__(self):

373

memory_storage = MemoryStorage()

374

375

self.conversation_state = ConversationState(memory_storage)

376

self.user_state = UserState(memory_storage)

377

378

# Create state set for batch operations

379

self.state_set = BotStateSet(self.conversation_state, self.user_state)

380

381

self.user_name_accessor = self.user_state.create_property("UserName")

382

self.turn_count_accessor = self.conversation_state.create_property("TurnCount")

383

384

async def on_message_activity(self, turn_context: TurnContext):

385

# Load all states at once

386

await self.state_set.load_all(turn_context)

387

388

# Work with state properties

389

user_name = await self.user_name_accessor.get(turn_context, lambda: "Anonymous")

390

turn_count = await self.turn_count_accessor.get(turn_context, lambda: 0)

391

392

# Update data

393

turn_count += 1

394

await self.turn_count_accessor.set(turn_context, turn_count)

395

396

if turn_context.activity.text.startswith("call me"):

397

new_name = turn_context.activity.text[8:].strip()

398

await self.user_name_accessor.set(turn_context, new_name)

399

user_name = new_name

400

401

# Save all states at once

402

await self.state_set.save_all_changes(turn_context)

403

404

reply = f"Hello {user_name}! This is turn #{turn_count}"

405

await turn_context.send_activity(MessageFactory.text(reply))

406

```

407

408

### State Clearing and Deletion

409

410

```python

411

async def on_message_activity(self, turn_context: TurnContext):

412

text = turn_context.activity.text.lower()

413

414

if text == "reset user":

415

# Clear user state

416

await self.user_state.clear_state(turn_context)

417

await self.user_state.save_changes(turn_context)

418

await turn_context.send_activity(MessageFactory.text("User state cleared!"))

419

420

elif text == "reset conversation":

421

# Clear conversation state

422

await self.conversation_state.clear_state(turn_context)

423

await self.conversation_state.save_changes(turn_context)

424

await turn_context.send_activity(MessageFactory.text("Conversation state cleared!"))

425

426

elif text == "delete my data":

427

# Delete specific property

428

await self.user_profile_accessor.delete(turn_context)

429

await self.user_state.save_changes(turn_context)

430

await turn_context.send_activity(MessageFactory.text("User profile deleted!"))

431

```

432

433

### Custom Default Value Factory

434

435

```python

436

def create_default_user_profile():

437

return {

438

"name": "New User",

439

"join_date": datetime.now().isoformat(),

440

"settings": {

441

"notifications": True,

442

"theme": "light"

443

}

444

}

445

446

async def on_message_activity(self, turn_context: TurnContext):

447

# Use factory function for complex default values

448

user_profile = await self.user_profile_accessor.get(

449

turn_context,

450

create_default_user_profile

451

)

452

453

# Now user_profile is guaranteed to have the structure we need

454

await turn_context.send_activity(

455

MessageFactory.text(f"Welcome back, {user_profile['name']}!")

456

)

457

```

458

459

## Types

460

461

```python { .api }

462

class PropertyManager:

463

"""Base class for managing properties."""

464

pass

465

466

class StoreItem:

467

"""Base class for items stored in bot state."""

468

def __init__(self):

469

self.e_tag = "*"

470

```