feat(phase8 #80 D8.6 chunk-2): wire UIMessageStore.write live + drop snapshot_assembler / agent_artifact#1723
Merged
Conversation
…snapshot_assembler / agent_artifact
Phase 8 D8.6 chunk-2 hard-cut Option 2 (PM lock msg=d916b44a): A+B in
one PR — read-side artifact-fallback removal + write-side
``UIMessageStore.write`` live wire-in. Pre-launch system has no users
/ no data, so destructive deletion replaces migration.
A — Read-side artifact-fallback removal
---------------------------------------
- Delete ``aperag/domains/agent_runtime/snapshot_assembler.py`` and
its test.
- ``services.py:get_turn_snapshot`` reads ``UIMessageStore.read``
only — no more ``query_agent_artifacts_by_turn`` fallback. FAILED
/ CANCELLED ``error_text`` falls back to the ``AgentTurn`` row's
``error_message``.
- ``chat_service.py:_build_v3_chat_history`` switches to per-turn
``UIMessageStore.read`` (lazy singleton, mirrors chunk-1's
``ChatTitleService`` pattern).
B — Write-side ``UIMessageStore.write`` live wire-in
----------------------------------------------------
- ``runtime.py`` end-of-turn now composes a single canonical
``UIMessage`` (``TextPart`` + ``SourceUrlPart`` /
``DataCitationPart`` per reference, mirroring the FE-bound
shape) and persists via ``uimessage_store.write``. Replaces both
``artifact_service.create_artifact(ANSWER, ...)`` and
``create_artifact(REFERENCE_BUNDLE, ...)``.
- FAILED path drops the ``error_summary`` artifact write — the
error surface stays on the ``AgentTurn`` row, which the snapshot
endpoint already reads.
- ``mark_completed`` loses the ``answer_artifact_id`` /
``reference_bundle_artifact_id`` parameters; the runtime-state
Redis merge no longer carries those fields.
- ``HistoryWriter.build_history_context`` reads canonical
``UIMessage`` text parts per-turn; legacy artifact lookup is
gone.
- Reader migration:
* ``ChatCompletionService._build_completion_content`` accepts a
parts list and projects the OpenAI-compat
``answer + DOC_QA_REFERENCES + json`` envelope from
``TextPart`` + ``DataCitationPart``.
* ``evaluation/worker._extract_answer_text`` joins ``TextPart``
text from the persisted message.
Deletions
---------
- ``ArtifactService`` class (no production callers after wire-in).
- ``/api/v2/agent/artifacts/{artifact_id}`` route +
``get_artifact_view``.
- ``AgentArtifact`` SQLAlchemy model + ``AgentArtifactType`` enum +
``AgentArtifactEnvelope`` schema (incl. its ``__all__`` export).
- ``AgentTurn.answer_artifact_id`` / ``reference_bundle_artifact_id``
columns + their indices.
- ``AgentTurnEnvelope`` artifact_id fields (no remaining consumer).
- ``db_ops`` methods: ``create_agent_artifact`` /
``query_agent_artifact`` / ``query_agent_artifacts_by_turn``.
- Helper ``_extract_answer_text_from_artifact`` in services.py.
Migration
---------
- New alembic head ``d8e6c2b4f1a9`` (revises ``c8f2d34a51e7``):
drops the ``agent_artifact`` table + its three indices and the
two ``agent_turn`` artifact_id columns + their indices. Downgrade
reconstructs both for rollback symmetry.
Caller sweep (``AgentArtifact`` / ``artifact_service`` /
``snapshot_assembler`` / ``extract_error_text`` /
``assemble_parts_from_artifacts`` / ``answer_artifact_id`` /
``reference_bundle_artifact_id``): zero remaining Python call-sites
in ``aperag/`` or ``tests/`` (only docstring narratives describing
the removal + a single bash comment in
``tests/e2e_http/scripts/run_chat_collection_flow.sh``).
Tests
-----
- Rewrote ``test_agent_runtime_v3.py`` + ``test_chat_service.py`` +
``test_chat_completion_service.py`` to drive the new code paths
via a minimal in-memory ``_FakeUIMessageStore``; dropped legacy
``_FakeArtifactService`` and ``query_agent_artifacts_by_turn``
mocks.
- Added ``test_agent_runtime_views_no_artifact_route`` regression
guard pinning the deletion of the ``/agent/artifacts/{id}`` route
+ ``get_artifact_view`` symbol.
- ``test_agent_runtime_openapi_contract.py`` now asserts the
artifact route + ``AgentArtifactEnvelope`` schema are absent from
the FastAPI OpenAPI spec.
- Full unit suite: 989 passed, 29 skipped, ruff + format clean.
Out of scope (chunk-3 per PM Option 2 lock)
-------------------------------------------
- ``agent_timeline_event`` table removal + replay/reload semantic
change. Wire-emitter event sourcing stays as-is in this PR.
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
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
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.
Summary
Task #80 D8.6 hard-cut chunk-2 per PM Option 2 lock (msg=d916b44a) — A+B in one PR:
snapshot_assembler.py+ tests, rip outassemble_parts_from_artifacts/extract_error_textfromservices.py:get_turn_snapshotandchat_service:_build_v3_chat_history.UIMessageStore.writeinto the agent runtime end-of-turn (composeUIMessage(parts=[TextPart, *(SourceUrlPart+DataCitationPart per ref)])), deleteArtifactService/agent_artifacttable //agent/artifacts/{id}route /AgentArtifactEnvelope/AgentArtifactTypeand migrate 4 readers (HistoryWriter, chat_completion_service, evaluation/worker, snapshot endpoint).agent_timeline_eventdeletion is chunk-3 (out of scope per PM lock).Diff
PM 3 review-face gates (msg=d916b44a + msg=c84e9fa4)
1.
UIMessageStore.writeis wired into live runtime (not test-only)aperag/domains/agent_runtime/runtime.py:436+: end-of-turn now callsawait self.uimessage_store.write(turn_id=..., chat_id=..., message=UIMessage(...))with parts composed by_compose_assistant_parts(turn_id, answer_text, references).AgentRuntimeTaskManager.__init__constructs the singleton viaUIMessageDbOps(session_factory=get_async_session)+AgentRuntimeRedisStore(); same instance is passed toTurnService,HistoryWriter, andPydanticAIRuntime.runtime_manager.uimessage_storeis exposed as a public attribute sochat_completion_serviceandevaluation/workercan read the canonical store directly.2. Read path still complete after artifact-fallback delete
services.py:get_turn_snapshot) readsawait self.uimessage_store.read(turn_id)— answer text + citations come back asTextPart+SourceUrlPart+DataCitationPart(D8 §2 byte-equal). FAILEDerror_textfalls back to theAgentTurnrow'serror_message.chat_service.py:_build_v3_chat_history) reads canonical UIMessage per-turn, lazy-builds the store with the same factory pattern aschat_title_service(chunk-1).chat_completion_service._build_completion_content) projectsTextPart+DataCitationPartinto the existinganswer + DOC_QA_REFERENCES + jsonenvelope shape.3. Write set strictly disjoint from chunk-3
agent_timeline_event/EventService.append_event/redis_store.append_event/get_events_after. SSE streaming + reload mechanism unchanged.runtime.pyretained (turn lifecycle + tool lifecycle + state.changed + text.delta).Caller sweep
Full grep across
aperag/+tests/forAgentArtifact*/artifact_service/create_artifact/query_agent_artifact*/snapshot_assembler/extract_error_text/assemble_parts_from_artifacts/answer_artifact_id/reference_bundle_artifact_id:tests/e2e_http/scripts/run_chat_collection_flow.sh:164(historical context).Migration
New alembic head
d8e6c2b4f1a9(revisesc8f2d34a51e7):Downgrade reconstructs the table + columns + indices for rollback symmetry.
Tests
test_agent_runtime_v3.py(3 snapshot tests + history writer test + view test) using_FakeUIMessageStore.test_chat_service.py(3 history tests) using_FakeUIMessageStore.test_chat_completion_service.py(3 OpenAI-compat tests) using_FakeUIMessageStore.test_agent_runtime_openapi_contract.pyto assert/agent/artifacts/{id}route +AgentArtifactEnvelopeschema are absent from FastAPI OpenAPI spec.test_agent_runtime_views_no_artifact_routeregression guard.Out of scope (chunk-3, separate PR)
agent_timeline_eventtable removalEventService.append_eventDB write removalTest plan
uv run pytest tests/unit_test/ -q— 989 passeduvx ruff check + format— clean