Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
47 changes: 47 additions & 0 deletions docs/user-guide/workflows/configuration/context-management.md
Original file line number Diff line number Diff line change
Expand Up @@ -233,6 +233,7 @@ task: |
- ⚠️ **Simple lists**: Not added to context as variables (use output_key or wrap in dict)
- ⚠️ **Plain strings**: Not added to context as variables (use output_key or output as JSON)
- 💡 **Best practice**: Always output structured JSON from assistants for maximum flexibility
- 💡 **Parallel iterations**: Use `append_to_context: true` with `output_key` to collect all iteration results into a list — without it, only the last iteration's value is retained

#### Accessing Context in Templates

Expand Down Expand Up @@ -337,6 +338,40 @@ states:
include_in_llm_history: false # Don't show raw IDs to LLM
```

**append_to_context** (boolean, default: `false`)

Controls whether the current state's output is appended to an existing list in the context store rather than overwriting it.

- `true`: Output is accumulated — the value under `output_key` in the context store grows into a list across multiple executions (most useful with `iter_key` iterations)
- `false` (default): Output overwrites the previous value under the same key

**When to use `true`:**

- Collecting results from all parallel `iter_key` iterations into a single list
- Any scenario where multiple states write to the same key and all values must be preserved

**Requirements and constraints:**

- Use together with `output_key` to specify which context key accumulates the results
- When `append_to_context: true`, the top-level state key (set by `output_key`) is **not** written — read the accumulated list from the context store only via `{{output_key}}`
- Has no effect when `store_in_context: false`

```yaml
states:
- id: process-item
assistant_id: processor
task: "Analyze {{task}} and return a result object"
next:
state_id: aggregate
output_key: results
append_to_context: true # Each parallel branch appends; context_store["results"] = [r1, r2, r3]

- id: aggregate
assistant_id: aggregator
task: "Summarize all results: {{results}}"
# {{results}} contains the full list from every iteration
```

**clear_prior_messages** (boolean, default: `false`)

Clears all prior messages from the message history, creating a "fresh start" for the LLM context.
Expand Down Expand Up @@ -543,6 +578,18 @@ next:

Use case: When iterating over items, prevent context from accumulating across iterations. Each iteration gets only the current item, not data from previous iterations.

**Pattern 7: Accumulate All Iteration Results Into a List**

```yaml
next:
state_id: aggregate
iter_key: items
output_key: all_results
append_to_context: true
```

Use case: When iterating in parallel and you need to collect every branch's output into a single list. Without `append_to_context: true`, only the last branch's value is kept (last-value-wins). With it, `context_store["all_results"]` contains a list with one entry per iteration.

### 6.3 Dynamic Value Resolution

Dynamic value resolution enables you to use context store values throughout your workflow configuration using template syntax.
Expand Down
84 changes: 80 additions & 4 deletions docs/user-guide/workflows/configuration/state-transitions.md
Original file line number Diff line number Diff line change
Expand Up @@ -361,6 +361,21 @@ The previous state can output various formats, and `iter_key` adapts accordingly
- Two formats: dictionary key (`"items"`) or JSON Pointer (`"/data/items"`)
- The extracted value must be a list or will be wrapped as single-item list

**append_to_context** (boolean, default: `false`):

- When `true`, each iteration's output is **appended** to a list in the context store instead of overwriting the previous value
- When `false` (default), standard overwrite semantics apply — the last iteration's value wins on duplicate keys
- Use together with `output_key` to control which context key accumulates the collected results
- When `append_to_context: true` is combined with `output_key`, the top-level state key is **not** set — values are only available via the accumulated list in the context store

```yaml
next:
state_id: collect-results
iter_key: items
output_key: processed_items
append_to_context: true # Each iteration appends its output; context_store["processed_items"] becomes a list
```

#### Multi-Stage Iteration:

For multi-stage iteration (when you have multiple sequential states processing each item), the **same `iter_key` must be present in every state** included in the iteration chain.
Expand Down Expand Up @@ -550,6 +565,57 @@ states:
4. After all branches complete, contexts and message histories are merged
5. Merged results flow to `merge-results`

**Example 7: Accumulating Results Across Iterations**

When all iteration results must be preserved, use `append_to_context: true` so each parallel branch contributes to a shared list rather than overwriting it.

```yaml
states:
- id: get-tickets
tool_id: jira-api
tool_args:
jql: "project = PROJ AND status = 'Open'"
# Outputs: [{"id": "PROJ-1", "title": "Bug A"}, {"id": "PROJ-2", "title": "Bug B"}, {"id": "PROJ-3", "title": "Bug C"}]
next:
state_id: analyze-ticket
iter_key: . # Iterate over the entire list

- id: analyze-ticket
assistant_id: analyzer
task: |
Analyze ticket {{id}}: {{title}}
Return a JSON object: {"ticket_id": "...", "severity": "low|medium|high", "summary": "..."}
# Iteration 1 returns: {"ticket_id": "PROJ-1", "severity": "high", "summary": "..."}
# Iteration 2 returns: {"ticket_id": "PROJ-2", "severity": "low", "summary": "..."}
# Iteration 3 returns: {"ticket_id": "PROJ-3", "severity": "medium", "summary": "..."}
next:
state_id: create-report
output_key: analyses
append_to_context: true # Accumulate all results; context_store["analyses"] = [{...}, {...}, {...}]

- id: create-report
assistant_id: reporter
task: |
Create a severity report based on all ticket analyses.
Analyses: {{analyses}}
# Receives the full list of all three analyses in {{analyses}}
next:
state_id: end
```

**How it works:**

1. Three parallel branches process one ticket each
2. Each branch writes its output with the `analyses` key via `append_to_context: true`
3. The reducer appends each result to the list — no overwriting occurs
4. `create-report` receives `analyses = [result_1, result_2, result_3]` in its context

:::tip
`append_to_context: true` is the recommended way to collect results from all parallel iterations into a single list. It replaces the workaround of using unique per-iteration keys (`result_1`, `result_2`, ...).
:::

---

#### Context Isolation and Merging:

Iterations have important context management characteristics that ensure proper isolation and aggregation:
Expand All @@ -571,9 +637,17 @@ Iterations have important context management characteristics that ensure proper

- When all parallel iterations complete (fan-in), their context stores are **merged**
- The merge uses `add_or_replace_context_store` reducer
- For duplicate keys across iterations, the **last value wins** (last iteration overwrites previous)
- **Default (overwrite)**: for duplicate keys across iterations, the **last value wins** (last iteration overwrites previous)
- **Accumulation mode**: when `append_to_context: true` is set on the iterating state, each iteration's output is **appended** to a list under the specified key — no values are lost
- The merged context is then passed to the next state after iteration

**Choosing between overwrite and accumulation:**

| Mode | Config | Result for key `output` after 3 iterations |
| ------------------- | -------------------------- | ----------------------------------------------- |
| Overwrite (default) | `append_to_context: false` | `output = "result_3"` (only last) |
| Accumulation | `append_to_context: true` | `output = ["result_1", "result_2", "result_3"]` |

**Message History Merging:**

- Similarly, message histories from all iterations are also merged
Expand Down Expand Up @@ -611,9 +685,9 @@ states:
**Important Context Merging Notes:**

- Iterations are isolated during execution but merged after completion
- Context keys set by multiple iterations will have only one final value (last wins)
- To preserve all iteration results, use unique keys (e.g., `result_1`, `result_2`) or aggregate into lists
- Message histories are fully preserved from all iterations
- By default, context keys set by multiple iterations will have only one final value (last wins)
- To preserve all iteration results, set `append_to_context: true` combined with `output_key` — the context key will accumulate all values as a list
- Message histories are fully preserved from all iterations regardless of the merge mode

#### Important Notes:

Expand All @@ -626,5 +700,7 @@ states:
- Cannot combine `iter_key` with `state_ids` (parallel transitions) or `condition`/`switch`
- Each iteration branch has isolated context and message history during execution
- After all iterations complete, contexts and message histories are merged using LangGraph reducers
- Use `append_to_context: true` to accumulate all iteration outputs into a list; without it, only the last iteration's value is retained for duplicate keys
- `append_to_context: true` has no effect when `store_in_context: false`

---
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
# How do I collect all results from parallel workflow iterations?

Use `append_to_context: true` together with `output_key` in the `next:` block of the
iterating state. This tells the workflow engine to accumulate each iteration's output into
a list rather than overwriting the previous value.

```yaml
states:
- id: process-item
assistant_id: processor
task: "Analyze {{task}} and return a result object"
next:
state_id: aggregate
iter_key: items
output_key: all_results
append_to_context: true

- id: aggregate
assistant_id: aggregator
task: "Summarize all results: {{all_results}}"
# {{all_results}} contains every iteration's output as a list
```

By default (`append_to_context: false`), when multiple parallel branches write to the
same context key, only the last branch's value is kept. Setting `append_to_context: true`
preserves all values in `context_store["all_results"]` as an ordered list.

## Sources

- [State Transitions — Iterative Transitions](https://docs.codemie.ai/user-guide/workflows/configuration/state-transitions)
- [Context Management — Context Control Flags](https://docs.codemie.ai/user-guide/workflows/configuration/context-management)
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
# What happens to context keys when multiple iterations write to the same key?

By default, when parallel `iter_key` iterations all write to the same context key, the
**last iteration's value wins** — earlier values are silently overwritten during the
fan-in merge.

To preserve every iteration's output, set `append_to_context: true` on the iterating
state. With this flag, each branch appends its output to a list instead of overwriting:

| Mode | Config | Result after 3 iterations writing to `output` |
| ------------------- | -------------------------- | ----------------------------------------------- |
| Overwrite (default) | `append_to_context: false` | `output = "result_3"` (only last) |
| Accumulation | `append_to_context: true` | `output = ["result_1", "result_2", "result_3"]` |

Use `append_to_context: true` together with `output_key` to name the accumulation key.
The accumulated list is accessible via `{{output_key}}` in subsequent states.

## Sources

- [State Transitions — Context Isolation and Merging](https://docs.codemie.ai/user-guide/workflows/configuration/state-transitions)
- [Context Management — Context Control Flags](https://docs.codemie.ai/user-guide/workflows/configuration/context-management)
Loading