Skip to content

fix(api): isolate side-effect session writes in multimodal and RAG handlers#38210

Open
agarwalpranav0711 wants to merge 2 commits into
langgenius:mainfrom
agarwalpranav0711:fix/isolate-side-effect-session-writes
Open

fix(api): isolate side-effect session writes in multimodal and RAG handlers#38210
agarwalpranav0711 wants to merge 2 commits into
langgenius:mainfrom
agarwalpranav0711:fix/isolate-side-effect-session-writes

Conversation

@agarwalpranav0711

Copy link
Copy Markdown

Important

  1. Make sure you have read our contribution guidelines
  2. Ensure there is an associated issue and you have been assigned to it
  3. Use the correct syntax to link this PR: Fixes #<issue number>.

Summary

Fixes #38176

Extends the session isolation fix from #37895 to two files that were missed: base_app_runner.py and index_tool_callback_handler.py.

Both files used db.session.commit() on the shared Flask request-scoped session to persist side-effect writes (a MessageFile during multimodal LLM streaming, and DatasetQuery audit logs / hit_count updates during RAG retrieval). This flushed all pending ORM changes in the request transaction, not just the intended record, risking premature commits and DetachedInstanceError under concurrency.

Changes:

  • base_app_runner.py: _handle_multimodal_image_content() now persists the MessageFile through an independent sessionmaker session instead of the shared session.
  • index_tool_callback_handler.py: on_query() and on_tool_end() now persist audit/analytics writes through an independent session, following the same pattern as fix(api): isolate side-effect database writes #37895.
  • Added/updated unit tests in both corresponding test files to cover the new session isolation behavior.

Screenshots

N/A - backend transaction/session fix

Checklist

  • This change requires a documentation update, included: Dify Document
  • I understand that this PR may be closed in case there was no previous discussion or issues. (This doesn't apply to typos!)
  • I've added a test for each change that was introduced, and I tried as much as possible to make a single atomic change.
  • I've updated the documentation accordingly.
  • I ran make lint && make type-check (backend) and cd web && pnpm exec vp staged (frontend) to appease the lint gods

…ndlers

Extends the session isolation fix from langgenius#37895 to two files that were
missed: base_app_runner.py and index_tool_callback_handler.py.

Both files used db.session.commit() on the shared Flask request-scoped
session to persist side-effect writes (a MessageFile during multimodal
LLM streaming, and DatasetQuery audit logs / hit_count updates during
RAG retrieval). This flushed all pending ORM changes in the request
transaction, not just the intended record, risking premature commits
and DetachedInstanceError under concurrency.

Both now use independent sessionmaker sessions matching the
established pattern from langgenius#37895.

Fixes langgenius#38176
@dosubot dosubot Bot added the size:M This PR changes 30-99 lines, ignoring generated files. label Jun 30, 2026
@github-actions

Copy link
Copy Markdown
Contributor

Pyrefly Diff

base → PR
--- /tmp/pyrefly_base.txt	2026-06-30 13:45:52.202826908 +0000
+++ /tmp/pyrefly_pr.txt	2026-06-30 13:45:43.870745082 +0000
@@ -4226,17 +4226,17 @@
 ERROR Object of class `NoneType` has no attribute `status` [missing-attribute]
    --> tests/unit_tests/core/base/test_app_generator_tts_publisher.py:188:16
 ERROR Argument `None` is not assignable to parameter `app_id` with type `str` in function `core.callback_handler.index_tool_callback_handler.DatasetIndexToolCallbackHandler.__init__` [bad-argument-type]
-  --> tests/unit_tests/core/callback_handler/test_index_tool_callback_handler.py:66:20
+  --> tests/unit_tests/core/callback_handler/test_index_tool_callback_handler.py:84:20
 ERROR Argument `None` is not assignable to parameter `message_id` with type `str` in function `core.callback_handler.index_tool_callback_handler.DatasetIndexToolCallbackHandler.__init__` [bad-argument-type]
-  --> tests/unit_tests/core/callback_handler/test_index_tool_callback_handler.py:67:24
+  --> tests/unit_tests/core/callback_handler/test_index_tool_callback_handler.py:85:24
 ERROR Argument `None` is not assignable to parameter `user_id` with type `str` in function `core.callback_handler.index_tool_callback_handler.DatasetIndexToolCallbackHandler.__init__` [bad-argument-type]
-  --> tests/unit_tests/core/callback_handler/test_index_tool_callback_handler.py:68:21
+  --> tests/unit_tests/core/callback_handler/test_index_tool_callback_handler.py:86:21
 ERROR Argument `None` is not assignable to parameter `invoke_from` with type `InvokeFrom` in function `core.callback_handler.index_tool_callback_handler.DatasetIndexToolCallbackHandler.__init__` [bad-argument-type]
-  --> tests/unit_tests/core/callback_handler/test_index_tool_callback_handler.py:69:25
+  --> tests/unit_tests/core/callback_handler/test_index_tool_callback_handler.py:87:25
 ERROR Argument `None` is not assignable to parameter `query` with type `str` in function `core.callback_handler.index_tool_callback_handler.DatasetIndexToolCallbackHandler.on_query` [bad-argument-type]
-  --> tests/unit_tests/core/callback_handler/test_index_tool_callback_handler.py:72:26
+  --> tests/unit_tests/core/callback_handler/test_index_tool_callback_handler.py:90:26
 ERROR Argument `None` is not assignable to parameter `dataset_id` with type `str` in function `core.callback_handler.index_tool_callback_handler.DatasetIndexToolCallbackHandler.on_query` [bad-argument-type]
-  --> tests/unit_tests/core/callback_handler/test_index_tool_callback_handler.py:72:32
+  --> tests/unit_tests/core/callback_handler/test_index_tool_callback_handler.py:90:32
 ERROR Argument `list[DummyToolInvokeMessage]` is not assignable to parameter `tool_outputs` with type `Iterable[ToolInvokeMessage]` in function `core.callback_handler.workflow_tool_callback_handler.DifyWorkflowCallbackHandler.on_tool_execution` [bad-argument-type]
   --> tests/unit_tests/core/callback_handler/test_workflow_tool_callback_handler.py:55:30
 ERROR Argument `list[DummyToolInvokeMessage]` is not assignable to parameter `tool_outputs` with type `Iterable[ToolInvokeMessage]` in function `core.callback_handler.workflow_tool_callback_handler.DifyWorkflowCallbackHandler.on_tool_execution` [bad-argument-type]
@@ -4581,7 +4581,7 @@
 ERROR Argument `SimpleNamespace` is not assignable to parameter `tenant` with type `Tenant` in function `core.plugin.backwards_invocation.model.PluginModelBackwardsInvocation.invoke_summary` [bad-argument-type]
   --> tests/unit_tests/core/plugin/test_backwards_invocation_model.py:54:72
 ERROR Generator function should return `Generator` [bad-return]
-  --> tests/unit_tests/core/plugin/test_model_runtime_adapter.py:72:51
+  --> tests/unit_tests/core/plugin/test_model_runtime_adapter.py:49:51
 ERROR Class member `_BadString.__str__` overrides a member in a parent class but is missing an `@override` decorator [missing-override-decorator]
    --> tests/unit_tests/core/plugin/test_plugin_entities.py:238:17
 ERROR Argument `TestPluginDaemonEntities.test_credential_type_helpers._FakeCredential` is not assignable to parameter `self` with type `CredentialType` in function `core.plugin.entities.plugin_daemon.CredentialType.get_name` [bad-argument-type]

@github-actions

Copy link
Copy Markdown
Contributor

Pyrefly Type Coverage

Metric Base PR Delta
Type coverage 51.58% 51.57% -0.01%
Strict coverage 51.10% 51.08% -0.01%
Typed symbols 31,008 30,993 -15
Untyped symbols 29,384 29,384 0
Modules 2935 2935 0

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

size:M This PR changes 30-99 lines, ignoring generated files.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Bug] Shared db.session used for side-effect writes in multimodal streaming and RAG callback handlers

1 participant