Skip to content

fix: remap chunk results after skipped messages - #283

Open
Kevin Turcios (KRRT7) wants to merge 7 commits into
microsoft:mainfrom
KRRT7:review-changes-pr277
Open

Kevin Turcios (KRRT7) wants to merge 7 commits into
microsoft:mainfrom
KRRT7:review-changes-pr277

Conversation

@KRRT7

@KRRT7 Kevin Turcios (KRRT7) commented Jun 9, 2026 •

Copy link
Copy Markdown
Contributor

Summary

  • Remap source-stream chunk locations to consecutive storage message ordinals when failed messages are skipped.
  • Preserve correct chunk embedding and semantic-reference locations for messages committed after a skipped message.
  • Add end-to-end coverage for a failed message followed by a successful one.
  • Retain regression coverage for graceful shutdown with a full chunk queue.

Validation

  • make format check test
  • GitHub CI and CodeQL

…Result docstring

The success property was removed in a previous commit but the docstring
still referenced it, causing confusion.
Propagate shutdown_event to dispatcher for coordinated pipeline shutdown.
When shutdown is requested, skip remaining chunks instead of processing them
after the producer has stopped.
@KRRT7
Kevin Turcios (KRRT7) force-pushed the review-changes-pr277 branch 2 times, most recently from 30d6b02 to 004ed74 Compare September 19, 2026 02:37
@KRRT7 Kevin Turcios (KRRT7) changed the title fix: add graceful shutdown support and prevent producer deadlock in add_messages pipeline test: cover graceful shutdown with a full chunk queue Sep 19, 2026
@KRRT7 Kevin Turcios (KRRT7) changed the title test: cover graceful shutdown with a full chunk queue fix: remap chunk results after skipped messages Sep 19, 2026
@KRRT7

Copy link
Copy Markdown
Contributor Author

Bernhard Merkle (@bmerkle) robgruen if you wouldn't mind reviewing, thanks



@pytest.mark.asyncio
async def test_streaming_skips_failed_message_and_commits_next_message() -> None:

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would maybe add one more test that includes a repair & reimport to make sure there are no duplicates.

something like this: fail -> repair -> replay -> replay

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've pushed new tests for that, hopefully they're good.

Adds the fail -> repair -> replay -> replay coverage requested in review,
to confirm a repaired reimport does not duplicate messages.

- ControlledExtractor gains fail_on_text: chunks are extracted concurrently,
  so call-index-based failure cannot deterministically target a specific
  message.  The set is mutable so a test can "repair" the extractor.
- _replay mirrors what importers do (tools/ingest_email.py): pre-filter
  already-ingested source IDs via are_sources_ingested, then stream the rest.
- test_streaming_replay_after_repair_ingests_each_source_once: the skipped
  message's source is never marked ingested, so a repaired replay picks up
  exactly that message and a second replay is a no-op.
- test_streaming_replay_without_repair_does_not_duplicate: replaying while
  the failure persists re-skips rather than duplicating.

@bmerkle Bernhard Merkle (bmerkle) left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Review summary

Ran a multi-pass review (correctness, test coverage, comment accuracy, error handling) against the diff. The core fix is sound, but the specific bug this PR claims to fix is not actually covered by a test, and there's one error-handling inconsistency worth closing before merge.

Critical Issues

None. The positional remap in _commit_batch_from_chunk_results was traced against the reassembler's invariants (contiguous per-message result grouping in chunk_results, monotonic ordinals, zero-chunk-message handling) and is correct given how add_messages.py's producer/reassembler currently stage results.

Important Issues

  1. The remap this PR fixes has no test verifying it end-to-end. Every extractor in the test suite returns an empty KnowledgeResponse, so add_knowledge_batch_to_semantic_ref_index never actually produces a SemanticRef, and no test reads transcript.semantic_refs/semantic_ref_index after a skip. A regression that reverted just the knowledge_items remap (lines 344-351) would pass every test currently in this PR. Suggest adding a test with a non-empty KnowledgeResponse (e.g. one entity per chunk, named after the chunk text) across a skip, then asserting the resulting semref's message_ordinal points at the correct storage message, not the source-stream one.
  2. Narrow scenario coverage. All skip tests cover exactly one skip of a single-chunk message. Worth adding cases for: two consecutive skips, a skip followed by a multi-chunk message, a zero-chunk message adjacent to a skip, and a replay that crosses a batch boundary — these are the shapes most likely to expose an off-by-one in result_group_index.
  3. Unguarded dict lookup breaks this function's own error-handling convention — see inline suggestion below.
  4. Positional pairing is inferred from group counts, not verified by identity. ChunkProcessingResult already carries a message: TMessage field; asserting result.message is message while pairing would catch a future reassembler regression that silently swapped two same-sized messages (a scenario the current count-only checks wouldn't catch).

Suggestions

  • Line 341-342: "Chunk results exceed staged messages with chunks" carries no numbers (consumed vs. expected group count) — worth adding for debuggability, consistent with the other guards in this function.
  • Line 303 comment ("Store embedding by source message/chunk location until commit remaps it") is a little misleading — the map itself is never remapped; it's read with the source key throughout, while a separate list is built in storage order.

Strengths

  • Transaction semantics are clean: every raise in this function happens before any DB-mutating call, inside a BEGIN IMMEDIATE/rollback-on-exception transaction — no path leaves partial state.
  • Exception propagation to the caller is fail-fast, no swallowing (asyncio.TaskGroup).
  • fail_on_text on ControlledExtractor is a well-justified addition for deterministic failure injection under concurrent extraction.
  • _replay() realistically mirrors the production importer pattern in tools/ingest_email.py.

(Note: add_messages.py's ChunkProcessingResult docstring still references a nonexistent success property — confirmed that file is unchanged by this PR/identical to main, so that's pre-existing and out of scope here, but worth a follow-up cleanup.)

Comment thread src/typeagent/knowpro/conversation_base.py Outdated
@bmerkle

Copy link
Copy Markdown
Collaborator

This PR conflicts with main after #300/#301 (the import-style cleanup landed on main in the meantime). I resolved it locally on a mirror of this branch:

bmerkle/typeagent-py@7802f0b — bmerkle@7802f0b
(branch: review-changes-pr277)

Only one real conflict, in tests/test_add_messages_streaming.py: both sides added an independent import on the same line (this branch added AddMessagesResult from interfaces_core; main's #300 replaced the kplib alias with a direct KnowledgeResponse import from knowledge_schema). Both symbols are used in the file, so the fix keeps both imports — no logic changes elsewhere; the rest of the overlap (add_messages.py, conversation_base.py, test_add_messages_pipeline.py) auto-merged cleanly.

Verified after merging: make format (no changes), make check (pyright, 0 errors on 3.12 and 3.15), and uv run pytest (760 passed).

Kevin Turcios (@KRRT7) feel free to cherry-pick/merge that commit into this branch, or I'm happy to open it as a fresh PR against main if that's easier.

@KRRT7

Copy link
Copy Markdown
Contributor Author

Bernhard Merkle (@bmerkle) why don't you open the PR against this branch so that it resolves it here and attribution is kept properly?

This branch has not been deployed

No deployments
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.

3 participants