or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

Files

docs

actions.mdcheckpoints.mdconfiguration.mdgraph-construction.mdgraph-execution.mdindex.mdprebuilt.mdstate-management.md

state-management.mddocs/

0

# State Management

1

2

Comprehensive state management system with channels, serialization, and update strategies. Handles complex state updates with merge functions, conflict resolution, and schema-based validation.

3

4

## Capabilities

5

6

### AgentState Base Class

7

8

Foundation for all graph state with key-value storage and update mechanisms.

9

10

```java { .api }

11

/**

12

* Base class for agent state with map-based data storage

13

* @param initData Initial state data as key-value map

14

*/

15

class AgentState {

16

AgentState(Map<String, Object> initData);

17

18

/**

19

* Returns unmodifiable view of state data

20

* @return Immutable map of current state

21

*/

22

Map<String, Object> data();

23

24

/**

25

* Retrieves typed value by key

26

* @param key State key to retrieve

27

* @return Optional containing value if present

28

*/

29

<T> Optional<T> value(String key);

30

}

31

```

32

33

**Usage Examples:**

34

35

```java

36

// Define custom state class

37

class MyAgentState extends AgentState {

38

public MyAgentState(Map<String, Object> initData) {

39

super(initData);

40

}

41

}

42

43

// Create and use state

44

Map<String, Object> initialData = Map.of(

45

"messages", new ArrayList<String>(),

46

"score", 0,

47

"active", true

48

);

49

50

MyAgentState state = new MyAgentState(initialData);

51

52

// Access state values

53

Optional<Integer> score = state.value("score");

54

Optional<List<String>> messages = state.value("messages");

55

Optional<Boolean> active = state.value("active");

56

57

// Get raw data

58

Map<String, Object> allData = state.data();

59

```

60

61

### State Update Operations

62

63

Static methods for updating state with channel-based merge strategies.

64

65

```java { .api }

66

/**

67

* Updates state with partial state using channels for merge logic

68

* @param state Current state map

69

* @param partialState State updates to apply

70

* @param channels Channel definitions for merge behavior

71

* @return New state map with updates applied

72

* @throws NullPointerException if state is null

73

*/

74

static Map<String, Object> updateState(Map<String, Object> state, Map<String, Object> partialState, Map<String, Channel<?>> channels);

75

76

/**

77

* Updates AgentState with partial state using channels

78

* @param state Current AgentState instance

79

* @param partialState State updates to apply

80

* @param channels Channel definitions for merge behavior

81

* @return New state map with updates applied

82

* @throws NullPointerException if state is null

83

*/

84

static Map<String, Object> updateState(AgentState state, Map<String, Object> partialState, Map<String, Channel<?>> channels);

85

```

86

87

**Usage Examples:**

88

89

```java

90

// Simple state update

91

Map<String, Object> currentState = Map.of("counter", 5, "name", "Alice");

92

Map<String, Object> updates = Map.of("counter", 10, "active", true);

93

Map<String, Object> newState = AgentState.updateState(currentState, updates, Map.of());

94

// Result: {counter=10, name=Alice, active=true}

95

96

// Update with channels for merge behavior

97

Map<String, Channel<?>> channels = Map.of(

98

"messages", Channels.appender(ArrayList::new),

99

"counter", (key, current, new_val) -> (Integer) current + (Integer) new_val

100

);

101

102

Map<String, Object> stateWithMessages = Map.of(

103

"messages", new ArrayList<>(List.of("Hello")),

104

"counter", 5

105

);

106

107

Map<String, Object> updatesWithMessages = Map.of(

108

"messages", List.of("World"),

109

"counter", 3

110

);

111

112

Map<String, Object> merged = AgentState.updateState(stateWithMessages, updatesWithMessages, channels);

113

// messages: ["Hello", "World"], counter: 8

114

```

115

116

### Special State Markers

117

118

Control state value lifecycle with special marker objects.

119

120

```java { .api }

121

// Constants for state control

122

static final Object MARK_FOR_REMOVAL; // Removes key from state

123

static final Object MARK_FOR_RESET; // Resets key to null/default

124

```

125

126

**Usage Examples:**

127

128

```java

129

// Remove state keys

130

Map<String, Object> updates = Map.of(

131

"temporary_data", AgentState.MARK_FOR_REMOVAL,

132

"cache", AgentState.MARK_FOR_RESET,

133

"new_value", "updated"

134

);

135

136

Map<String, Object> cleaned = AgentState.updateState(currentState, updates, Map.of());

137

// temporary_data is completely removed, cache is set to null

138

```

139

140

### Channel System

141

142

Define how state values are updated and merged during graph execution.

143

144

```java { .api }

145

/**

146

* Interface defining state value update behavior

147

* @param <T> Type of values handled by this channel

148

*/

149

interface Channel<T> {

150

/**

151

* Updates a state value using channel-specific merge logic

152

* @param key State key being updated

153

* @param currentValue Current value (may be null)

154

* @param newValue New value to merge

155

* @return Updated value after applying channel logic

156

*/

157

Object update(String key, Object currentValue, Object newValue);

158

}

159

```

160

161

**Channel Implementations:**

162

163

```java { .api }

164

// Utility class for creating standard channels

165

class Channels {

166

/**

167

* Creates appender channel for collections

168

* @param factory Supplier creating empty collection instances

169

* @return Channel that appends new values to collections

170

*/

171

static <C extends Collection<Object>> Channel<C> appender(Supplier<C> factory);

172

}

173

174

// Appender channel for list-like state values

175

class AppenderChannel<C extends Collection<Object>> implements Channel<C> {

176

AppenderChannel(Supplier<C> factory);

177

Object update(String key, Object currentValue, Object newValue);

178

}

179

```

180

181

**Usage Examples:**

182

183

```java

184

import org.bsc.langgraph4j.state.Channels;

185

186

// Define schema with channels

187

Map<String, Channel<?>> schema = Map.of(

188

// Append new messages to existing list

189

"messages", Channels.appender(ArrayList::new),

190

191

// Custom counter channel that adds values

192

"counter", (key, current, newVal) -> {

193

int currentInt = (current != null) ? (Integer) current : 0;

194

int newInt = (Integer) newVal;

195

return currentInt + newInt;

196

},

197

198

// Last-writer-wins channel (default behavior)

199

"status", (key, current, newVal) -> newVal

200

);

201

202

// Use schema in StateGraph

203

StateGraph<MyAgentState> workflow = new StateGraph<>(schema, MyAgentState::new);

204

```

205

206

### State Factory Pattern

207

208

Define how state instances are created and initialized.

209

210

```java { .api }

211

/**

212

* Factory interface for creating agent state instances

213

* @param <State> Type of agent state to create

214

*/

215

@FunctionalInterface

216

interface AgentStateFactory<State extends AgentState> extends Function<Map<String, Object>, State> {

217

/**

218

* Creates state instance from data map

219

* @param data Initial state data

220

* @return New agent state instance

221

*/

222

State apply(Map<String, Object> data);

223

224

/**

225

* Creates initial data from schema channels

226

* @param channels Channel schema defining state structure

227

* @return Map with initial values based on schema

228

*/

229

default Map<String, Object> initialDataFromSchema(Map<String, Channel<?>> channels) {

230

// Default implementation returns empty map

231

return Map.of();

232

}

233

}

234

```

235

236

**Usage Examples:**

237

238

```java

239

// Simple factory using constructor reference

240

AgentStateFactory<MyAgentState> factory = MyAgentState::new;

241

242

// Custom factory with default values

243

AgentStateFactory<MyAgentState> factoryWithDefaults = (data) -> {

244

Map<String, Object> defaultData = Map.of(

245

"messages", new ArrayList<String>(),

246

"score", 0,

247

"created", System.currentTimeMillis()

248

);

249

250

// Merge provided data with defaults

251

Map<String, Object> merged = new HashMap<>(defaultData);

252

merged.putAll(data);

253

254

return new MyAgentState(merged);

255

};

256

257

// Factory with schema-based initialization

258

AgentStateFactory<MyAgentState> schemaFactory = new AgentStateFactory<MyAgentState>() {

259

@Override

260

public MyAgentState apply(Map<String, Object> data) {

261

return new MyAgentState(data);

262

}

263

264

@Override

265

public Map<String, Object> initialDataFromSchema(Map<String, Channel<?>> channels) {

266

Map<String, Object> initial = new HashMap<>();

267

268

// Initialize collections for appender channels

269

for (Map.Entry<String, Channel<?>> entry : channels.entrySet()) {

270

if (entry.getValue() instanceof AppenderChannel) {

271

initial.put(entry.getKey(), new ArrayList<>());

272

}

273

}

274

275

return initial;

276

}

277

};

278

```

279

280

### State Serialization

281

282

Control how state is serialized for checkpoints and persistence.

283

284

```java { .api }

285

/**

286

* Interface for state serialization and cloning

287

* @param <State> Type of agent state

288

*/

289

interface StateSerializer<State extends AgentState> {

290

/**

291

* Get the state factory used by this serializer

292

* @return State factory for creating instances

293

*/

294

AgentStateFactory<State> stateFactory();

295

296

/**

297

* Clone state object for immutability and persistence

298

* @param data State data to clone

299

* @return Cloned state instance

300

* @throws IOException, ClassNotFoundException, InstantiationException, IllegalAccessException

301

*/

302

State cloneObject(Map<String, Object> data) throws IOException, ClassNotFoundException, InstantiationException, IllegalAccessException;

303

}

304

```

305

306

**Serializer Implementations:**

307

308

```java { .api }

309

// Java object stream serialization

310

class ObjectStreamStateSerializer<State extends AgentState> implements StateSerializer<State> {

311

ObjectStreamStateSerializer(AgentStateFactory<State> stateFactory);

312

AgentStateFactory<State> stateFactory();

313

State cloneObject(Map<String, Object> data) throws IOException, ClassNotFoundException;

314

}

315

316

// JSON-based serialization with Jackson

317

class JacksonStateSerializer<State extends AgentState> extends PlainTextStateSerializer<State> {

318

JacksonStateSerializer(AgentStateFactory<State> stateFactory);

319

JacksonStateSerializer(AgentStateFactory<State> stateFactory, ObjectMapper objectMapper);

320

}

321

322

// JSON-based serialization with Gson

323

class GsonStateSerializer<State extends AgentState> extends PlainTextStateSerializer<State> {

324

GsonStateSerializer(AgentStateFactory<State> stateFactory);

325

GsonStateSerializer(AgentStateFactory<State> stateFactory, Gson gson);

326

}

327

```

328

329

**Usage Examples:**

330

331

```java

332

// Object stream serializer

333

StateSerializer<MyAgentState> objectSerializer =

334

new ObjectStreamStateSerializer<>(MyAgentState::new);

335

336

// Jackson JSON serializer

337

ObjectMapper mapper = new ObjectMapper();

338

StateSerializer<MyAgentState> jacksonSerializer =

339

new JacksonStateSerializer<>(MyAgentState::new, mapper);

340

341

// Gson JSON serializer

342

Gson gson = new GsonBuilder().create();

343

StateSerializer<MyAgentState> gsonSerializer =

344

new GsonStateSerializer<>(MyAgentState::new, gson);

345

346

// Use in StateGraph

347

StateGraph<MyAgentState> workflow = new StateGraph<>(schema, jacksonSerializer);

348

```

349

350

### State Snapshots

351

352

Immutable state representations with execution context.

353

354

```java { .api }

355

/**

356

* Immutable snapshot of graph state at specific execution point

357

*/

358

class StateSnapshot<State extends AgentState> implements NodeOutput<State> {

359

/**

360

* Creates state snapshot from checkpoint and configuration

361

* @param checkpoint Checkpoint data

362

* @param config Runtime configuration

363

* @param stateFactory Factory for creating state instances

364

* @return State snapshot instance

365

*/

366

static <State extends AgentState> StateSnapshot<State> of(Checkpoint checkpoint, RunnableConfig config, AgentStateFactory<State> stateFactory);

367

368

// NodeOutput interface methods

369

String node();

370

State state();

371

372

// Snapshot-specific methods

373

String getCheckpointId();

374

String getNodeId();

375

String getNextNodeId();

376

RunnableConfig getConfig();

377

}

378

```

379

380

**Usage Examples:**

381

382

```java

383

// Access snapshot information

384

StateSnapshot<MyAgentState> snapshot = app.getState(config);

385

386

System.out.println("Execution at node: " + snapshot.getNodeId());

387

System.out.println("Next node will be: " + snapshot.getNextNodeId());

388

System.out.println("Checkpoint ID: " + snapshot.getCheckpointId());

389

390

// Access state data

391

MyAgentState currentState = snapshot.state();

392

Map<String, Object> stateData = currentState.data();

393

394

// Get execution context

395

RunnableConfig executionConfig = snapshot.getConfig();

396

```

397

398

## Advanced State Patterns

399

400

### Reducer Pattern

401

402

Implement custom merge logic for complex state updates.

403

404

```java { .api }

405

interface Reducer<T> {

406

T reduce(T current, T newValue);

407

}

408

409

// Custom channel with reducer

410

Channel<Integer> sumChannel = (key, current, newVal) -> {

411

int currentInt = (current != null) ? (Integer) current : 0;

412

return currentInt + (Integer) newVal;

413

};

414

415

Channel<Set<String>> setUnionChannel = (key, current, newVal) -> {

416

Set<String> currentSet = (current != null) ? (Set<String>) current : new HashSet<>();

417

Set<String> result = new HashSet<>(currentSet);

418

if (newVal instanceof Collection) {

419

result.addAll((Collection<String>) newVal);

420

} else {

421

result.add((String) newVal);

422

}

423

return result;

424

};

425

```

426

427

### Conditional State Updates

428

429

Implement state updates with conditional logic.

430

431

```java { .api }

432

// Conditional update channel

433

Channel<String> conditionalStatus = (key, current, newVal) -> {

434

String newStatus = (String) newVal;

435

String currentStatus = (String) current;

436

437

// Only update if new status has higher priority

438

Map<String, Integer> priorities = Map.of(

439

"pending", 1,

440

"processing", 2,

441

"completed", 3,

442

"failed", 4

443

);

444

445

int currentPriority = priorities.getOrDefault(currentStatus, 0);

446

int newPriority = priorities.getOrDefault(newStatus, 0);

447

448

return (newPriority >= currentPriority) ? newStatus : currentStatus;

449

};

450

```

451

452

### State Validation

453

454

Validate state updates during channel processing.

455

456

```java { .api }

457

// Validating channel

458

Channel<Integer> validatedCounter = (key, current, newVal) -> {

459

Integer newValue = (Integer) newVal;

460

if (newValue < 0) {

461

throw new IllegalArgumentException("Counter cannot be negative");

462

}

463

return newValue;

464

};

465

466

// Range-bound channel

467

Channel<Double> boundedValue = (key, current, newVal) -> {

468

Double value = (Double) newVal;

469

return Math.max(0.0, Math.min(100.0, value)); // Clamp to [0, 100]

470

};

471

```