Skip to content

LCORE-1573: surface compaction outcome as context_status in query responses - #2443

Open
max-svistunov wants to merge 2 commits into
lightspeed-core:mainfrom
max-svistunov:lcore-1573-context-status
Open

LCORE-1573: surface compaction outcome as context_status in query responses#2443
max-svistunov wants to merge 2 commits into
lightspeed-core:mainfrom
max-svistunov:lcore-1573-context-status

Conversation

@max-svistunov

Copy link
Copy Markdown
Contributor

Description

Add a context_status field ("full" / "summarized") to query responses so clients know whether conversation compaction occurred (LCORE-1573, part of the conversation-compaction feature, LCORE-1631 epic).

The field is added in the two places clients actually receive on the wire:

  • QueryResponse (src/models/api/responses/successful/query.py) — the non-streaming /v1/query response body.
  • EndEventData (src/models/common/agents/stream_payloads.py) — the SSE end event payload for /v1/streaming_query, alongside the analogous truncated signal. StreamingQueryResponse is a documentation-only class with an empty body, so the field is intentionally NOT added there; only its SSE example string is updated.

The value maps directly from CompactionResult.compacted (set by the LCORE-1572 integration) via a new context_status property on CompactionResult. The non-streaming endpoint reads it when building QueryResponse; the streaming compaction-aware path captures it from the yielded CompactionResult and threads it through generate_agent_response (new parameter, default "full") into EndStreamPayload.create. The shared ContextStatus Literal type alias lives in models/common/turn_summary.py.

/v1/responses intentionally does not get the field (stays OpenAI-shaped, compacts silently per R12); the A2A executor is out of scope.

The compaction design doc (docs/design/conversation-compaction/conversation-compaction.md) is updated to record the two-place split, and docs/devel_doc/openapi.json is regenerated (ContextStatus appears as a named enum schema referenced by QueryResponse.context_status; the streaming endpoint's SSE example shows context_status in the end event).

Type of change

  • Refactor
  • New feature
  • Bug fix
  • CVE fix
  • Optimization
  • Documentation Update
  • Configuration Update
  • Bump-up service version
  • Bump-up dependent library
  • Bump-up library or tool used for development (does not change the final image)
  • CI configuration change
  • Konflux configuration change
  • Unit tests improvement
  • Integration tests improvement
  • End to end tests improvement
  • Benchmarks improvement

Tools used to create PR

Identify any AI code assistants used in this PR (for transparency and review context)

  • Assisted-by: Claude Opus 4.8
  • Generated by: Claude Opus 4.8

Related Tickets & Documents

  • Related Issue # LCORE-1573
  • Closes # LCORE-1573

Checklist before requesting a review

  • I have performed a self-review of my code.
  • PR has passed all pre-merge test jobs.
  • If it is a core feature, I have added thorough tests.

Testing

  1. Start the full local stack (Llama Stack + Lightspeed Stack, compaction disabled or defaults) and send a non-streaming query:

    curl -s -X POST http://localhost:8080/v1/query -H "Content-Type: application/json" \
      -d '{"query": "What is Kubernetes? Answer in one sentence."}'
    

    Expected: response JSON includes "context_status": "full".
    Actual (verified locally against llama-stack 0.6.0):

    context_status: full
    conversation_id: 778541b3698e8d3c1043ec3366ab8f94ae627e9d3aa193d9
    
  2. Connect to the streaming endpoint and inspect the SSE end event:

    curl -s -N -X POST http://localhost:8080/v1/streaming_query -H "Content-Type: application/json" \
      -d '{"query": "What is a container image? One sentence."}'
    

    Expected: the end event's data includes context_status.
    Actual (verified locally):

    data: {"event": "end", "data": {"referenced_documents": [], "truncated": null,
           "context_status": "full", "input_tokens": 25, "output_tokens": 44}, ...}
    
  3. The "summarized" value could not be verified live: compacted-mode requests (explicit-input rewrite) currently fail with HTTP 500 on both /v1/query and /v1/streaming_query before the response is built — a pre-existing issue in the agent pipeline (retrieve_agent_response passes responses_params.input through cast(str, ...) into agent.run(), but in compacted mode input is an item list). Filed as LCORE-3582. The compacted → "summarized" mapping is covered by unit tests at every layer (model, CompactionResult property, end-event payload, and both endpoint pipelines).

  4. Run the unit tests specific to this change:

    uv run pytest tests/unit -k context_status -q
    

    Result: 10 passed.

  5. Run the full suites:

    uv run make test-unit          # 3222 passed, 1 skipped, coverage 90.44%
    uv run python -m pytest tests/integration --ignore=tests/integration/container_lifecycle -q
                                   # 260 passed, 1 xfailed (container_lifecycle skipped: no container runtime)
    
  6. Regenerate and inspect the OpenAPI schema:

    uv run make schema
    python3 -c "import json; s=json.load(open('docs/devel_doc/openapi.json'))['components']['schemas']; \
      print(s['QueryResponse']['properties']['context_status'], s['ContextStatus'])"
    

    Expected/Actual: context_status present on QueryResponse with $ref to the ContextStatus enum (["full", "summarized"]); the /v1/streaming_query SSE example shows "context_status": "full" in the end event.

@coderabbitai

coderabbitai Bot commented Aug 17, 2026

Copy link
Copy Markdown
Contributor

Warning

Review limit reached

@max-svistunov, you've reached your PR review limit, so we couldn't start this review.

Next review available in: 7 minutes

Limit details: You’ve used all 1 included review currently available under your plan.

You've used all free OSS reviews for now. Wait for the free limit to reset to keep reviewing this public repository.

How can I continue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews.

How do review limits work?

CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability.

For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window.

Please refer docs for additional details.

Review details
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: ba80f379-bc71-470d-9e6b-97995de20569

📥 Commits

Reviewing files that changed from the base of the PR and between f9e5343 and a3cc753.

📒 Files selected for processing (15)
  • docs/design/conversation-compaction/conversation-compaction.md
  • docs/devel_doc/openapi.json
  • src/app/endpoints/query.py
  • src/app/endpoints/streaming_query.py
  • src/models/api/responses/successful/query.py
  • src/models/common/__init__.py
  • src/models/common/agents/stream_payloads.py
  • src/models/common/turn_summary.py
  • src/utils/agents/streaming.py
  • src/utils/conversation_compaction.py
  • tests/unit/app/endpoints/test_query.py
  • tests/unit/app/endpoints/test_streaming_query.py
  • tests/unit/models/responses/test_query_response.py
  • tests/unit/utils/agents/test_streaming.py
  • tests/unit/utils/test_conversation_compaction.py

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

…ponses

Add a context_status field ("full" when no compaction occurred,
"summarized" when older turns were replaced by a compaction summary) to
the two response surfaces clients actually receive on the wire:

- QueryResponse (src/models/api/responses/successful/query.py) for the
  non-streaming /v1/query endpoint.
- EndEventData (src/models/common/agents/stream_payloads.py), the SSE
  "end" event payload, for the streaming /v1/streaming_query endpoint.
  StreamingQueryResponse is a documentation-only class with an empty
  body, so the field is deliberately NOT added there; only its SSE
  example string is updated to show context_status in the end event.

The value maps directly from CompactionResult.compacted (set by the
LCORE-1572 compaction integration): a new context_status property on
CompactionResult performs the mapping in one place. The non-streaming
endpoint reads it when building QueryResponse; the streaming
compaction-aware path captures it from the yielded CompactionResult and
threads it through generate_agent_response (new context_status
parameter, defaulting to "full" for the non-compaction path) into
EndStreamPayload.create.

The shared ContextStatus Literal["full", "summarized"] type alias lives
in models/common/turn_summary.py (imported by both response surfaces
already) and is exported from models.common. In the regenerated OpenAPI
schema it becomes a named enum component referenced by
QueryResponse.context_status; the streaming endpoint's SSE example now
shows context_status in the end event (the streaming response is
documented via an inline example only, so EndEventData itself does not
appear as a component schema).

/v1/responses intentionally does not get the field (it stays
OpenAI-shaped and compacts silently by design, R12), and the A2A
executor is out of scope for the UI-indicator use case.

Unit tests cover the CompactionResult mapping, the QueryResponse field
(default, explicit value, rejection of unknown values), the end event
payload contents for both statuses, and the full/summarized threading
through both endpoint pipelines.
…n doc

Update the conversation-compaction design doc to match the implemented
context_status surface:

- Rewrite the "API response changes" section: the field is added to
  QueryResponse (non-streaming /v1/query) and EndEventData (the
  streaming SSE end event payload), not to StreamingQueryResponse,
  which turned out to be a documentation-only class with an empty
  body — adding a field there would change nothing on the wire, so it
  is intentionally skipped and only its SSE example is updated.
- Replace the stale "src/models/responses.py (now relocated)" row in
  the key-files table with the two real locations and the
  docs-only-skip note.
- Update the request-flow step 11 note now that LCORE-1573 has landed.
@max-svistunov
max-svistunov force-pushed the lcore-1573-context-status branch from f6b5bbb to a3cc753 Compare August 17, 2026 10:25
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant