Repository navigation
LCORE-3788: keep a turn a shield blocked on /v1/responses out of what compaction sends to the model #2996
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Open
max-svistunov
wants to merge
3
commits into
lightspeed-core:main
Choose a base branch
from
max-svistunov:lcore-3788-shield-blocked-replay-part1
base: main
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Open
LCORE-3788: keep a turn a shield blocked on /v1/responses out of what compaction sends to the model #2996
Changes from all commits
Commits
Show all changes
3 commits
Select commit
Hold shift + click to select a range
File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,100 @@ | ||
| """The mark of a turn a shield blocked (LCORE-3788). | ||
|
|
||
| A turn a shield blocked is stored in the conversation, so that the history a | ||
| client reads is complete. It must not reach the model again. Conversation | ||
| compaction rebuilds the model's context from the stored items, so a blocked | ||
| text that is replayed or summarized there was not blocked, only delayed by one | ||
| turn. | ||
|
|
||
| A conversation item has no field for metadata, so the mark is the item id: the | ||
| messages of a blocked turn are stored under an id that starts with | ||
| ``BLOCKED_ITEM_ID_PREFIX``. OGX keeps an id the caller supplies. The ids it | ||
| generates itself are ``msg_`` followed by hexadecimal digits or a UUID, so they | ||
| never start with the prefix. | ||
|
|
||
| Only turns stored with the mark are recognized. A blocked turn stored before | ||
| the mark existed looks like any other turn. | ||
| """ | ||
|
|
||
| import secrets | ||
| from collections.abc import Sequence | ||
| from typing import Any, Final | ||
|
|
||
| BLOCKED_ITEM_ID_PREFIX: Final[str] = "msg_blocked_" | ||
| """Start of the id of every message that belongs to a blocked turn.""" | ||
|
|
||
|
|
||
| def new_blocked_item_id() -> str: | ||
| """Return a new id for a message of a blocked turn. | ||
|
|
||
| Item ids are unique across all conversations in OGX, and storing an item | ||
| under an id that exists overwrites that item. So the id is random, with as | ||
| many random bytes as OGX uses for the ids it generates. | ||
|
|
||
| Returns: | ||
| ``BLOCKED_ITEM_ID_PREFIX`` followed by 48 hexadecimal digits. | ||
|
|
||
| """ | ||
| return f"{BLOCKED_ITEM_ID_PREFIX}{secrets.token_hex(24)}" | ||
|
|
||
|
|
||
| def is_blocked_item(item: Any) -> bool: | ||
| """Tell whether a stored conversation item belongs to a blocked turn. | ||
|
|
||
| Parameters: | ||
| item: A conversation item, as OGX lists it. | ||
|
|
||
| Returns: | ||
| True when the item has an id that starts with | ||
| ``BLOCKED_ITEM_ID_PREFIX``. | ||
|
|
||
| """ | ||
| item_id = getattr(item, "id", None) | ||
| return isinstance(item_id, str) and item_id.startswith(BLOCKED_ITEM_ID_PREFIX) | ||
|
|
||
|
|
||
| def exclude_blocked_items(items: Sequence[Any]) -> list[Any]: | ||
| """Return the conversation items that do not belong to a blocked turn. | ||
|
|
||
| This is for code that builds what the model reads. A client that reads the | ||
| conversation gets the blocked turns as well. | ||
|
|
||
| A summary marker records how many stored items it covers, blocked ones | ||
| included. Apply this filter after the items were cut at that count, never | ||
| before. | ||
|
|
||
| Parameters: | ||
| items: Conversation items, as stored. | ||
|
|
||
| Returns: | ||
| The items that are not blocked: the same objects, in their original | ||
| order. | ||
|
|
||
| """ | ||
| return [item for item in items if not is_blocked_item(item)] | ||
|
|
||
|
|
||
| def mark_blocked(items: Sequence[dict[str, Any]]) -> list[dict[str, Any]]: | ||
| """Mark the items of a turn as blocked, before they are stored. | ||
|
|
||
| Every message gets a new blocked id, in place of the id it may have had: | ||
| the input of a request can carry ids the client chose. Items of other | ||
| types are left as they are; the model's context is built from messages | ||
| only. | ||
|
|
||
| Parameters: | ||
| items: The items of the turn, as dictionaries ready to be stored. | ||
|
|
||
| Returns: | ||
| New dictionaries, in the same order; the ones passed in are not | ||
| modified. | ||
|
|
||
| """ | ||
| return [ | ||
| ( | ||
| {**item, "id": new_blocked_item_id()} | ||
| if item.get("type") == "message" | ||
| else dict(item) | ||
| ) | ||
| for item in items | ||
| ] |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Script executed:
Repository: lightspeed-core/lightspeed-stack
Length of output: 8744
Script executed:
Repository: lightspeed-core/lightspeed-stack
Length of output: 17000
Script executed:
Repository: lightspeed-core/lightspeed-stack
Length of output: 9536
Script executed:
Repository: lightspeed-core/lightspeed-stack
Length of output: 11432
Use explicit input when blocked items exist without a summary marker.
When a conversation has blocked items, no summary marker, and no cached summaries,
_load_compaction_stateremoves them fromrecent_items. If the follow-up stays below the threshold,apply_compactionreturns unchanged parameters withconversationintact./v1/responsespasses those parameters to OGX, so the blocked items can be reloaded for the model.The regression test does not cover this case. It enables compaction but seeds
_compacted_conversation(), which already contains a summary marker. Add a below-threshold follow-up test that uses_plain_conversation()with compaction enabled. Ensure blocked items trigger explicit input even when no summary is needed.