Skip to content

Commit 2e9d335

Browse files
committed
LCORE-1675: Documentation for conversation compaction
Document the conversation compaction feature across OpenAPI spec, configuration guide, architecture overview, and query endpoint docs. Add context_status field ("full"/"summarized") to QueryResponse and StreamingQueryResponse documentation. Create comprehensive user guide at docs/user_doc/conversation_compaction.md with configuration examples, behavior details, and FAQ.
1 parent 296495d commit 2e9d335

7 files changed

Lines changed: 316 additions & 14 deletions

File tree

docs/README.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,8 @@ See the full documentation at [`../README.md`](../README.md) or browse sub-pages
2929

3030
[OKP guide](https://lightspeed-core.github.io/lightspeed-stack/user_doc/okp_guide.html)
3131

32+
[Conversation compaction](https://lightspeed-core.github.io/lightspeed-stack/user_doc/conversation_compaction.html)
33+
3234
[Authentication and Authorization](https://lightspeed-core.github.io/lightspeed-stack/user_doc/auth.html)
3335

3436
[User data collection](https://lightspeed-core.github.io/lightspeed-stack/user_doc/user_data_collection.html)

docs/devel_doc/ARCHITECTURE.md

Lines changed: 84 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@ To keep requests on-topic and protect sensitive data, LCore applies **safety shi
3535
- **Multi-Provider Support**: Works with multiple LLM providers (Ollama, OpenAI, Watsonx, etc.)
3636
- **Enterprise Security**: Authentication, authorization (RBAC), and secure credential management
3737
- **Resource Management**: Token-based quota limits and usage tracking
38-
- **Conversation Management**: Multi-turn conversations with history and caching
38+
- **Conversation Management**: Multi-turn conversations with history, caching, and automatic compaction
3939
- **RAG Integration**: Retrieval-Augmented Generation for context-aware responses
4040
- **Tool Orchestration**: Model Context Protocol (MCP) server integration
4141
- **Observability**: Prometheus metrics, structured logging, and health checks
@@ -370,6 +370,83 @@ External A2A requests go through LCore's standard authentication system (K8s, RH
370370

371371
---
372372

373+
### 2.11 Conversation Compaction (`utils/compaction.py`, `utils/conversation_compaction.py`)
374+
375+
**Purpose:** Automatically summarize older conversation turns when the conversation history approaches the LLM's context window limit, preventing HTTP 413 failures and enabling arbitrarily long conversations.
376+
377+
**Design Philosophy (Option A):** Once compaction triggers, LCore takes ownership of the context sent to the LLM. The `conversation` parameter is dropped from the OGX call (`omit_conversation=True`), and LCore constructs the input explicitly from summaries + recent turns + new query. The full original history remains in OGX for auditing.
378+
379+
**Architecture:**
380+
381+
The compaction system is split into two layers:
382+
383+
1. **Pure Logic Layer** (`utils/compaction.py`) — Side-effect-free functions:
384+
- `partition_conversation()` — Splits conversation items into old and recent chunks using a *degrading guard*: starts with the configured `buffer_turns` and shrinks one pair at a time until the recent chunk fits the token budget
385+
- `summarize_chunk()` — Single LLM call to produce a `ConversationSummary` from older turns
386+
- `recursively_resummarize()` — Folds multiple accumulated summaries into one when they approach the context limit
387+
388+
2. **Runtime Integration Layer** (`utils/conversation_compaction.py`) — Manages side effects:
389+
- Per-conversation locking (serializes concurrent requests on the same conversation)
390+
- Compaction state loading (cache-preferred with marker fallback)
391+
- Marker persistence (`[lightspeed:compaction-summary]` sentinel in conversation items)
392+
- `CompactionStartedEvent` emission for streaming progress indicators
393+
- `apply_compaction()` (async generator) — Main entry point used by all endpoints
394+
- `store_compacted_turn()` — Appends user query + LLM output when in compacted mode
395+
396+
**Data Flow:**
397+
398+
```
399+
User Query → Estimate Tokens → Exceeds Threshold?
400+
401+
No │ Yes
402+
↓ │ ↓
403+
Pass-through Acquire Lock
404+
405+
Fetch Conversation Items
406+
407+
Load Compaction State
408+
(cache → marker fallback)
409+
410+
Partition (old | recent)
411+
412+
Summarize Old Chunk (LLM call)
413+
414+
Write Marker + Cache Summary
415+
416+
Recursive Fold (if needed)
417+
418+
Build Explicit Input:
419+
[summaries + recent + query]
420+
421+
Set omit_conversation=True
422+
423+
Release Lock → Continue to LLM
424+
```
425+
426+
**Endpoint Integration:**
427+
428+
| Endpoint | Mode | Cache | `context_status` |
429+
|---|---|---|---|
430+
| `/v1/query` | Blocking (`apply_compaction_blocking()`) | Yes | Yes (`"full"` / `"summarized"`) |
431+
| `/v1/streaming_query` | Streaming (`apply_compaction()` generator) | Yes | Yes (in `end` event) |
432+
| `/v1/responses` | Blocking | Yes | No (OpenAI-compatible, silent) |
433+
| `/a2a` | Blocking, marker-only (no cache) | No | No (A2A protocol scope) |
434+
435+
**Configuration:**
436+
437+
Compaction is controlled by `CompactionConfiguration` in `lightspeed-stack.yaml`:
438+
- `enabled` (default: `false`) — Master switch
439+
- `threshold_ratio` (default: `0.7`) — Fraction of context window that triggers compaction
440+
- `token_floor` (default: `4096`) — Minimum token count before compaction can fire
441+
- `buffer_turns` (default: `4`) — Recent turns kept verbatim
442+
- `buffer_max_ratio` (default: `0.3`) — Max fraction of window for the buffer
443+
444+
Models must have context windows registered via `inference.context_windows` (a map of model ID to token count).
445+
446+
**Concurrency:** A per-conversation lock dictionary serializes concurrent compaction requests on the same conversation. Lock entries are reference-counted and cleaned up when the last waiter exits.
447+
448+
---
449+
373450
## 3. Request Processing Pipeline
374451

375452
This section illustrates how requests flow through LCore from initial receipt to final response.
@@ -400,11 +477,12 @@ Here's how a real query flows through the system:
400477
5. **Model Selection** - Use configured default model (e.g., `meta-llama/Llama-3.1-8B-Instruct`)
401478
6. **Context Building** - Retrieve conversation history, query RAG vector stores for relevant docs, determine available MCP tools
402479
7. **Shield moderation** - LCore-owned direct-run moderation (and agent capabilities where applicable) using shields configured in LCORE config
403-
8. **Llama Stack / agent call** - Send request with system prompt, RAG context, and MCP tools
404-
9. **LLM Processing** - Stack / agent generates response, may invoke MCP tools, returns token counts
405-
10. **Post-Processing** - Generate conversation summary if new
406-
11. **Store Results** - Save to Cache DB, User DB, consume quota, update metrics
407-
12. **Return Response** - Complete LLM response with referenced documents, token usage, and remaining quota
480+
8. **Conversation compaction** - If enabled and estimated tokens exceed the threshold, summarize older turns and rebuild the context (see [Section 2.11](#211-conversation-compaction-utilscompactionpy-utilsconversation_compactionpy))
481+
9. **OGX / agent call** - Send request with system prompt, RAG context, and MCP tools
482+
10. **LLM Processing** - Stack / agent generates response, may invoke MCP tools, returns token counts
483+
11. **Post-Processing** - Generate conversation summary if new
484+
12. **Store Results** - Save to Cache DB, User DB, consume quota, update metrics
485+
13. **Return Response** - Complete LLM response with referenced documents, token usage, and remaining quota
408486

409487
**Key Takeaways:**
410488
- RAG enhances responses with relevant documentation

docs/devel_doc/openapi.md

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2738,7 +2738,7 @@ user's query to a selected Llama Stack LLM and returning the generated response.
27382738
- mcp_headers: Headers that should be passed to MCP servers.
27392739

27402740
### Returns:
2741-
- QueryResponse: Contains the conversation ID and the LLM-generated response.
2741+
- QueryResponse: Contains the conversation ID, the LLM-generated response, and a `context_status` field indicating whether the conversation context is `"full"` or `"summarized"`.
27422742

27432743
### Raises:
27442744
- HTTPException:
@@ -3021,7 +3021,7 @@ content type text/event-stream.
30213021
- mcp_headers: Headers that should be passed to MCP servers.
30223022

30233023
### Returns:
3024-
- SSE-formatted events for the query lifecycle.
3024+
- SSE-formatted events for the query lifecycle. Includes a `context_status` field (`"full"` or `"summarized"`) in the `end` event payload indicating whether conversation compaction was applied. When compaction is triggered, a `compaction` SSE event is emitted before inference begins.
30253025

30263026
### Raises:
30273027
- HTTPException:
@@ -8010,6 +8010,7 @@ Attributes:
80108010
| available_quotas | object | Quota available as measured by all configured quota limiters |
80118011
| tool_calls | array | List of tool calls made during response generation |
80128012
| tool_results | array | List of tool results |
8013+
| context_status | string | Indicates whether the conversation context sent to the LLM is `"full"` (complete history) or `"summarized"` (older turns were summarized). Only present in QueryResponse and StreamingQueryResponse; omitted from `/v1/responses` (OpenAI-compatible) and `/a2a` responses. |
80138014

80148015

80158016
## QuotaExceededResponse

docs/devel_doc/query_endpoint.md

Lines changed: 9 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -145,6 +145,7 @@ The optional `solr` field configures Solr inline RAG behavior:
145145
| `tool_results` | array[object] | `[]` | Tool call results |
146146
| `rag_chunks` | array[object] | `[]` | *(Deprecated)* RAG chunks used |
147147
| `truncated` | boolean | `false` | *(Deprecated)* Always `false` |
148+
| `context_status` | string | `"full"` | Whether the conversation context is `"full"` (complete history) or `"summarized"` (older turns were summarized via conversation compaction) |
148149

149150
**`referenced_documents` items:**
150151

@@ -242,7 +243,7 @@ Emitted when the full response is assembled.
242243

243244
#### 7. `end`
244245

245-
Emitted last on success. Contains metadata.
246+
Emitted last on success. Contains metadata including `context_status` (`"full"` or `"summarized"`).
246247

247248
```json
248249
{
@@ -251,7 +252,8 @@ Emitted last on success. Contains metadata.
251252
"referenced_documents": [],
252253
"truncated": null,
253254
"input_tokens": 11,
254-
"output_tokens": 19
255+
"output_tokens": 19,
256+
"context_status": "full"
255257
},
256258
"available_quotas": {"UserQuotaLimiter": 998911}
257259
}
@@ -327,9 +329,9 @@ Both endpoints share the same pre-processing pipeline:
327329
11. Prepare Responses API parameters (model, system prompt, tools, MCP headers)
328330
12. Extract image attachments separately for multimodal input construction
329331

330-
**`/v1/query` then:** applies conversation compaction (blocking), calls the LLM, generates topic summary, consumes tokens, stores results, returns JSON.
332+
**`/v1/query` then:** applies conversation compaction (blocking), calls the LLM, generates topic summary, consumes tokens, stores results, returns JSON. When compaction is applied, the response includes `context_status: "summarized"`; otherwise `context_status: "full"`.
331333

332-
**`/v1/streaming_query` then:** generates a `request_id`, starts the SSE stream, emits events as the LLM generates tokens, performs post-stream cleanup (topic summary, token consumption, persistence).
334+
**`/v1/streaming_query` then:** generates a `request_id`, starts the SSE stream, applies compaction if needed (emitting a `compaction` SSE event), emits events as the LLM generates tokens, performs post-stream cleanup (topic summary, token consumption, persistence). The `end` event includes `context_status` indicating whether compaction was applied.
333335

334336
---
335337

@@ -409,7 +411,8 @@ curl -X POST http://localhost:8090/v1/query \
409411
"tool_calls": [],
410412
"tool_results": [],
411413
"rag_chunks": [],
412-
"truncated": false
414+
"truncated": false,
415+
"context_status": "full"
413416
}
414417
```
415418

@@ -500,7 +503,7 @@ data: {"event": "token", "data": {"id": 2, "token": " an"}}
500503
501504
data: {"event": "turn_complete", "data": {"id": 50, "token": "Kubernetes is an open-source..."}}
502505
503-
data: {"event": "end", "data": {"referenced_documents": [], "truncated": null, "input_tokens": 11, "output_tokens": 50}, "available_quotas": {"UserQuotaLimiter": 998950}}
506+
data: {"event": "end", "data": {"referenced_documents": [], "truncated": null, "input_tokens": 11, "output_tokens": 50, "context_status": "full"}, "available_quotas": {"UserQuotaLimiter": 998950}}
504507
```
505508

506509
### Streaming Query Interrupt

docs/index.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,8 @@ product questions using backend LLM services, agents, and RAG databases.
3434

3535
[OKP guide](https://lightspeed-core.github.io/lightspeed-stack/user_doc/okp_guide.html)
3636

37+
[Conversation compaction](https://lightspeed-core.github.io/lightspeed-stack/user_doc/conversation_compaction.html)
38+
3739
[Authentication and Authorization](https://lightspeed-core.github.io/lightspeed-stack/user_doc/auth.html)
3840

3941
[User data collection](https://lightspeed-core.github.io/lightspeed-stack/user_doc/user_data_collection.html)

docs/user_doc/config.md

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -224,6 +224,51 @@ Attributes:
224224
| buffer_turns | integer | Number of recent turns to keep verbatim. |
225225
| buffer_max_ratio | number | Maximum fraction of context window the buffer zone can occupy, regardless of buffer_turns. |
226226

227+
### How to enable conversation compaction
228+
229+
Compaction is disabled by default. To enable it, add a `compaction` section to your `lightspeed-stack.yaml` and set `enabled: true`. You must also register context window sizes for the models you use via the `inference.context_windows` map so the compaction trigger can calculate when older turns should be summarized.
230+
231+
**Minimal configuration:**
232+
233+
```yaml
234+
inference:
235+
default_provider: openai
236+
default_model: gpt-4o-mini
237+
context_windows:
238+
openai/gpt-4o-mini: 128000
239+
240+
compaction:
241+
enabled: true
242+
```
243+
244+
**Full configuration with all options:**
245+
246+
```yaml
247+
inference:
248+
default_provider: openai
249+
default_model: gpt-4o-mini
250+
context_windows:
251+
openai/gpt-4o-mini: 128000
252+
openai/gpt-4o: 128000
253+
254+
compaction:
255+
enabled: true
256+
threshold_ratio: 0.7 # trigger at 70% of context window (default)
257+
token_floor: 4096 # minimum tokens before compaction can fire (default)
258+
buffer_turns: 4 # recent turns kept verbatim (default)
259+
buffer_max_ratio: 0.3 # buffer may use at most 30% of the window (default)
260+
```
261+
262+
**Key considerations:**
263+
264+
- `context_windows` is required. Models absent from this map have no registered window and compaction will not trigger for them.
265+
- `threshold_ratio` controls how aggressively compaction fires. Lower values compact sooner; higher values wait longer (closer to the window limit).
266+
- `buffer_turns` sets how many recent user/assistant turn pairs are kept in full. A degrading guard automatically reduces this if the buffer itself would exceed `buffer_max_ratio` of the window.
267+
- `token_floor` prevents compaction from triggering on very short conversations.
268+
- When compaction is disabled (the default), requests that exceed the context window surface as HTTP 413.
269+
270+
For a comprehensive explanation of the feature, see the [Conversation Compaction Guide](conversation_compaction.md).
271+
227272

228273
## Configuration
229274

0 commit comments

Comments
 (0)