mirror of
https://github.com/agentscope-ai/ReMe.git
synced 2026-11-14 09:01:22 +00:00
Fix nested hint transcript retry stability
This commit is contained in:
parent
f3c07e7569
commit
11da577104
2 changed files with 84 additions and 20 deletions
|
|
@ -153,26 +153,38 @@ def _canonical_message_json(msg: Msg) -> str:
|
|||
return json.dumps(persisted, ensure_ascii=False, sort_keys=True, separators=(",", ":"))
|
||||
|
||||
|
||||
def _stabilize_content_block(raw_block: dict, path: tuple[int, ...], message_timestamp: str | None) -> dict:
|
||||
"""Stabilize defaults generated while parsing a raw content block."""
|
||||
block = dict(raw_block)
|
||||
if isinstance(block.get("hint"), list):
|
||||
block["hint"] = [
|
||||
_stabilize_content_block(child, (*path, index), message_timestamp) if isinstance(child, dict) else child
|
||||
for index, child in enumerate(block["hint"])
|
||||
]
|
||||
|
||||
if "id" not in block:
|
||||
canonical = json.dumps(raw_block, ensure_ascii=False, sort_keys=True, separators=(",", ":"), default=str)
|
||||
position = str(path[0]) if len(path) == 1 else ".".join(map(str, path))
|
||||
block["id"] = hashlib.sha256(f"{position}:{canonical}".encode("utf-8")).hexdigest()
|
||||
if "created_at" not in block and message_timestamp:
|
||||
block["created_at"] = message_timestamp
|
||||
if block.get("type") == "hint" and "finished_at" not in block and message_timestamp:
|
||||
block["finished_at"] = message_timestamp
|
||||
return block
|
||||
|
||||
|
||||
def _stabilize_raw_content_ids(item: dict) -> dict:
|
||||
"""Give raw content blocks stable IDs when the caller did not supply one."""
|
||||
"""Stabilize generated IDs and timestamps in raw (including nested hint) blocks."""
|
||||
content = item.get("content")
|
||||
if not isinstance(content, list):
|
||||
return item
|
||||
|
||||
stable_content = []
|
||||
changed = False
|
||||
for index, raw_block in enumerate(content):
|
||||
if not isinstance(raw_block, dict):
|
||||
stable_content.append(raw_block)
|
||||
continue
|
||||
block = dict(raw_block)
|
||||
if "id" not in block:
|
||||
canonical = json.dumps(block, ensure_ascii=False, sort_keys=True, separators=(",", ":"), default=str)
|
||||
block["id"] = hashlib.sha256(f"{index}:{canonical}".encode("utf-8")).hexdigest()
|
||||
if "created_at" not in block and item.get("created_at"):
|
||||
block["created_at"] = item["created_at"]
|
||||
stable_content.append(block)
|
||||
changed = changed or block != raw_block
|
||||
timestamp = item.get("created_at")
|
||||
stable_content = [
|
||||
_stabilize_content_block(block, (index,), timestamp) if isinstance(block, dict) else block
|
||||
for index, block in enumerate(content)
|
||||
]
|
||||
changed = stable_content != content
|
||||
return {**item, "content": stable_content} if changed else item
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -264,14 +264,29 @@ async def test_metadata_bool_to_int_correction_is_persisted(step, append_new):
|
|||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_identical_raw_retry_with_generated_content_ids_does_not_rewrite(step, monkeypatch):
|
||||
"""Parsing the same raw messages twice should not create false block-ID changes."""
|
||||
@pytest.mark.parametrize(
|
||||
"content",
|
||||
[
|
||||
[{"type": "text", "text": "same raw payload"}],
|
||||
[{"type": "hint", "hint": "notification"}],
|
||||
[
|
||||
{
|
||||
"type": "hint",
|
||||
"hint": [{"type": "text", "text": "notification"}],
|
||||
"finished_at": "2026-10-01T10:00:00",
|
||||
},
|
||||
],
|
||||
],
|
||||
ids=["text", "hint", "nested hint"],
|
||||
)
|
||||
async def test_identical_raw_retry_with_generated_content_defaults_does_not_rewrite(step, monkeypatch, content):
|
||||
"""Parsing identical raw messages twice should not churn generated block defaults."""
|
||||
raw = {
|
||||
"id": "m0",
|
||||
"name": "Alice",
|
||||
"role": "user",
|
||||
"name": "Assistant",
|
||||
"role": "assistant",
|
||||
"created_at": "2026-10-01T10:00:00",
|
||||
"content": [{"type": "text", "text": "same raw payload"}],
|
||||
"content": content,
|
||||
}
|
||||
first = step._build_messages([raw])
|
||||
await step._save_session_messages(_SESSION, first)
|
||||
|
|
@ -292,6 +307,43 @@ async def test_identical_raw_retry_with_generated_content_ids_does_not_rewrite(s
|
|||
assert step._session_path(_SESSION).read_bytes() == original
|
||||
|
||||
|
||||
def test_raw_hint_conversion_preserves_explicit_nested_ids_and_timestamps(step):
|
||||
"""Caller-provided IDs and timestamps remain authoritative at every hint depth."""
|
||||
message = step._to_msg(
|
||||
{
|
||||
"id": "m0",
|
||||
"name": "Alice",
|
||||
"role": "assistant",
|
||||
"created_at": "2026-10-01T10:00:00",
|
||||
"content": [
|
||||
{
|
||||
"type": "hint",
|
||||
"id": "outer-id",
|
||||
"created_at": "2026-10-01T10:00:01",
|
||||
"finished_at": "2026-10-01T10:00:02",
|
||||
"hint": [
|
||||
{
|
||||
"type": "text",
|
||||
"text": "notification",
|
||||
"id": "nested-id",
|
||||
"created_at": "2026-10-01T10:00:03",
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
)
|
||||
|
||||
outer = message.content[0]
|
||||
nested = outer.hint[0]
|
||||
assert (outer.id, outer.created_at, outer.finished_at) == (
|
||||
"outer-id",
|
||||
"2026-10-01T10:00:01",
|
||||
"2026-10-01T10:00:02",
|
||||
)
|
||||
assert (nested.id, nested.created_at) == ("nested-id", "2026-10-01T10:00:03")
|
||||
|
||||
|
||||
def test_raw_message_conversion_preserves_explicit_content_block_ids(step):
|
||||
"""Caller-provided block IDs remain authoritative during normalization."""
|
||||
message = step._to_msg(
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue