UN-3266 [FIX] Async execution backend stabilization#1903
UN-3266 [FIX] Async execution backend stabilization#1903harini-venkataraman wants to merge 152 commits intomainfrom
Conversation
Conflicts resolved: - docker-compose.yaml: Use main's dedicated dashboard_metric_events queue for worker-metrics - PromptCard.jsx: Keep tool_id matching condition from our async socket feature - PromptRun.jsx: Merge useEffect import from main with our branch - ToolIde.jsx: Keep fire-and-forget socket approach (spinner waits for socket event) - SocketMessages.js: Keep both session-store and socket-custom-tool imports + updateCusToolMessages dep - SocketContext.js: Keep simpler path-based socket connection approach - usePromptRun.js: Keep Celery fire-and-forget with socket delivery over polling - setupProxy.js: Accept main's deletion (migrated to Vite)
for more information, see https://pre-commit.ci
… into feat/execution-backend
for more information, see https://pre-commit.ci
… into feat/execution-backend
Add a defensive guard in `UsageHelper.get_usage_by_model()` that drops `Usage` rows where `usage_type == "llm"` and `llm_usage_reason` is empty. Per the Usage model contract, an empty reason is only valid when `usage_type == "embedding"`; an empty reason combined with `usage_type == "llm"` is a producer-side bug (an LLM call site forgot to pass `llm_usage_reason` in `usage_kwargs`). Without this guard the row surfaces in API deployment responses as a malformed bare `"llm"` bucket with no token breakdown alongside the legitimate `"extraction_llm"` bucket. The guard logs a warning on every dropped row so future producer regressions are detectable. Adds three regression tests in `backend/usage_v2/tests/test_helper.py` that stub `account_usage.models` and `usage_v2.models` in `sys.modules` so the helper can be imported without Django being set up: - `test_unlabeled_llm_row_is_dropped` — bare "llm" bucket disappears - `test_embedding_row_is_preserved` — guard is scoped to LLM rows - `test_all_three_llm_reasons_coexist` — extraction/challenge/summarize Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
for more information, see https://pre-commit.ci
There was a problem hiding this comment.
🧹 Nitpick comments (2)
backend/usage_v2/tests/test_helper.py (2)
129-164: Consider adding a test case forllm_usage_reason=None.The test uses an empty string
""for the unlabeled case (line 146), but the model definition (fromusage_v2/models.py) hasnull=True, meaningllm_usage_reasoncan also beNonein the database. While Python'snot llm_reasonhandles both, explicitly testing theNonecase ensures the defensive guard works for both representations.💡 Add test case for None value
def test_unlabeled_llm_row_is_dropped() -> None: """An ``llm`` row with empty ``llm_usage_reason`` must not produce a bare ``"llm"`` bucket in the response — it should be silently dropped, while the legitimate extraction row is preserved. """ _stub_rows( [ _row( usage_type="llm", llm_reason="extraction", sum_input=100, sum_output=50, sum_total=150, sum_cost=0.05, ), _row( usage_type="llm", llm_reason="", # the bug — no reason set sum_cost=0.01, ), + _row( + usage_type="llm", + llm_reason=None, # also test null case from DB + model_name="gpt-4o-mini", + sum_cost=0.01, + ), ] )Note: You'll also need to update the
_rowfunction's type hint fromllm_reason: strtollm_reason: str | Noneto support this.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@backend/usage_v2/tests/test_helper.py` around lines 129 - 164, Add an explicit None-case to the unlabeled LLM test and update the helper signature: modify test_unlabeled_llm_row_is_dropped to include an additional stub row where llm_reason is None (in addition to the empty-string case) so the code path handling missing reasons is validated, and change the _row helper function's type hint from llm_reason: str to llm_reason: str | None so it accepts None; keep assertions against UsageHelper.get_usage_by_model unchanged to ensure the unlabeled row is dropped and the extraction_llm entry remains.
33-70: Consider adding test cleanup for module stubs.The
sys.modulesmanipulation persists beyond the test module's lifetime. If other test modules in the same pytest session later importusage_v2.modelsexpecting the real Django model, they'll get the stub instead. This might be acceptable given the stated constraints, but consider adding a cleanup mechanism or documenting this limitation.💡 Optional: Add cleanup via pytest fixture or atexit
import atexit _original_modules: dict[str, Any] = {} def _install_stubs() -> tuple[Any, Any]: # ... existing code ... # Track originals for potential cleanup for mod_name in ["account_usage", "account_usage.models", "usage_v2.models"]: if mod_name not in _original_modules: _original_modules[mod_name] = sys.modules.get(mod_name) # ... rest of function ... def _cleanup_stubs() -> None: for mod_name, original in _original_modules.items(): if original is None: sys.modules.pop(mod_name, None) else: sys.modules[mod_name] = original atexit.register(_cleanup_stubs)🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@backend/usage_v2/tests/test_helper.py` around lines 33 - 70, The test installs module stubs in _install_stubs that persist in sys.modules and can leak into other tests; add cleanup to restore originals by capturing originals for keys ["account_usage","account_usage.models","usage_v2.models"] (e.g. _original_modules) when installing and implement a _cleanup_stubs function that restores or removes each entry from sys.modules, then register that cleanup (either via atexit.register(_cleanup_stubs) or expose a pytest fixture that calls _cleanup_stubs after tests) so UsageHelper/FakeUsage stubs do not leak across the pytest session.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Nitpick comments:
In `@backend/usage_v2/tests/test_helper.py`:
- Around line 129-164: Add an explicit None-case to the unlabeled LLM test and
update the helper signature: modify test_unlabeled_llm_row_is_dropped to include
an additional stub row where llm_reason is None (in addition to the empty-string
case) so the code path handling missing reasons is validated, and change the
_row helper function's type hint from llm_reason: str to llm_reason: str | None
so it accepts None; keep assertions against UsageHelper.get_usage_by_model
unchanged to ensure the unlabeled row is dropped and the extraction_llm entry
remains.
- Around line 33-70: The test installs module stubs in _install_stubs that
persist in sys.modules and can leak into other tests; add cleanup to restore
originals by capturing originals for keys
["account_usage","account_usage.models","usage_v2.models"] (e.g.
_original_modules) when installing and implement a _cleanup_stubs function that
restores or removes each entry from sys.modules, then register that cleanup
(either via atexit.register(_cleanup_stubs) or expose a pytest fixture that
calls _cleanup_stubs after tests) so UsageHelper/FakeUsage stubs do not leak
across the pytest session.
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro
Run ID: a3c5d643-3287-413e-895a-aa27cb7d475e
📒 Files selected for processing (3)
backend/usage_v2/helper.pybackend/usage_v2/tests/__init__.pybackend/usage_v2/tests/test_helper.py
- legacy_executor: extract _run_pipeline_answer_step helper to drop _handle_structure_pipeline cognitive complexity from 18 to under 15 - legacy_executor: bundle 9 prompt-run scalars into a prompt_run_args dict so _run_line_item_extraction has 8 params (was 15, limit 13) - legacy_executor: merge implicitly concatenated log string - structure_tool_task: extract _write_pipeline_outputs helper used by both _execute_structure_tool_impl and _run_agentic_extraction to remove the duplicated INFILE / COPY_TO_FOLDER write block (fixes the 6.1% duplication on new code) - test_context_retrieval_metrics: use pytest.approx for float compare, drop unused executor local, drop always-true if is_single_pass Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
There was a problem hiding this comment.
Actionable comments posted: 1
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
workers/executor/executors/legacy_executor.py (1)
1310-1325:⚠️ Potential issue | 🟡 MinorInconsistent EMAIL vs DATE null handling.
_convert_scalar_answernow correctly returnsNonefor NA cases (used by DATE), but the EMAIL type handling at lines 1848-1861 does not use this helper and still assigns the rawanswerwhen it's "NA" (line 1850:structured_output[prompt_name] = answer). This means EMAIL fields may contain the string"NA"while DATE fields getNone.Consider using
_convert_scalar_answerfor EMAIL as well for consistency:🔧 Proposed fix for consistent EMAIL handling
elif output_type == PSKeys.EMAIL: - if answer.lower() == "na": - structured_output[prompt_name] = answer - else: - email_prompt = ( - f"Extract the email from the following text:\n{answer}" - f"\n\nOutput just the email. " - f"The email should be directly assignable to a string " - f"variable. No explanation is required. If you cannot " - f'extract the email, output "NA".' - ) - structured_output[prompt_name] = answer_prompt_svc.run_completion( - llm=llm, prompt=email_prompt - ) + email_prompt = ( + f"Extract the email from the following text:\n{answer}" + f"\n\nOutput just the email. " + f"The email should be directly assignable to a string " + f"variable. No explanation is required. If you cannot " + f'extract the email, output "NA".' + ) + structured_output[prompt_name] = LegacyExecutor._convert_scalar_answer( + answer, llm, answer_prompt_svc, email_prompt + )Also applies to: 1848-1861
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@workers/executor/executors/legacy_executor.py` around lines 1310 - 1325, The EMAIL handling code assigns the raw answer (which can be the string "NA") directly to structured_output[prompt_name]; update that block to use the existing helper _convert_scalar_answer(answer, llm, answer_prompt_svc, prompt) so that "NA" is normalized to None like DATE does—call _convert_scalar_answer with the same llm, answer_prompt_svc, and prompt variables used elsewhere and assign its return value to structured_output[prompt_name] (allowing None to be stored when extraction fails).
🧹 Nitpick comments (1)
workers/executor/executors/legacy_executor.py (1)
1947-1994: Redundant file read for metrics injection may add unnecessary I/O overhead.This method re-reads the entire file solely to measure timing, then discards the content. For large files or remote storage (S3, GCS), this adds unnecessary I/O overhead and latency. Additionally, the measured time won't reflect what the cloud plugin actually experienced (different network conditions, caching, etc.).
Consider these alternatives:
- Have the cloud
single_pass_extractionplugin returncontext_retrievaltiming in its metrics (ideal)- If the plugin cannot be modified, accept that these metrics are estimates or skip injection entirely
The broad
Exceptioncatch (Ruff BLE001) is acceptable here since this is best-effort metrics injection with proper logging on failure.💡 Alternative: Skip re-read if not meaningful
If the timing measurement doesn't need to be accurate (just a rough estimate), consider caching the result or documenting that this is an approximation:
def _inject_context_retrieval_metrics( self, result: ExecutionResult, context: ExecutionContext ) -> None: - """Inject ``context_retrieval`` timing into single-pass metrics. + """Inject approximate ``context_retrieval`` timing into single-pass metrics. The cloud single_pass_extraction plugin handles retrieval internally but does not report ``context_retrieval`` timing in its returned metrics. This method replicates the file-read measurement from ``RetrievalService.retrieve_complete_context`` - and injects it into ``result.data["metrics"]``. + and injects it into ``result.data["metrics"]``. + + NOTE: This re-reads the file, so timing is an approximation and + adds I/O overhead. Consider updating the cloud plugin to report + this metric directly. """🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@workers/executor/executors/legacy_executor.py` around lines 1947 - 1994, The current _inject_context_retrieval_metrics method performs an extra fs.read via FileUtils.get_fs_instance to time file reads, causing redundant I/O; remove the file read and measurement logic and instead skip injecting a synthetic "context_retrieval" metric when the plugin did not provide it: check result.data[PSKeys.METRICS] and if no per-prompt "context_retrieval" exists, simply return (optionally emit a debug log and do not mutate data), leaving real timing to the cloud single_pass_extraction plugin or to a future opt-in estimation flag; update the method to stop calling fs.read and remove timing variables (start/elapsed) and the try/except block while preserving use of PSKeys, result, and context for locating the code.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@workers/executor/executors/legacy_executor.py`:
- Around line 1761-1769: The except KeyError handler that raises
LegacyExecutorError should chain the original KeyError to preserve traceback: in
the except block where you call ExecutorRegistry.get("line_item") and currently
raise LegacyExecutorError, modify the raise to include "from e" (or the caught
exception variable) so the new LegacyExecutorError is raised with exception
chaining from the original KeyError; update the except clause to capture the
KeyError as a variable (e.g., except KeyError as e) and raise
LegacyExecutorError(...) from e to satisfy Ruff B904 and preserve the original
traceback.
---
Outside diff comments:
In `@workers/executor/executors/legacy_executor.py`:
- Around line 1310-1325: The EMAIL handling code assigns the raw answer (which
can be the string "NA") directly to structured_output[prompt_name]; update that
block to use the existing helper _convert_scalar_answer(answer, llm,
answer_prompt_svc, prompt) so that "NA" is normalized to None like DATE
does—call _convert_scalar_answer with the same llm, answer_prompt_svc, and
prompt variables used elsewhere and assign its return value to
structured_output[prompt_name] (allowing None to be stored when extraction
fails).
---
Nitpick comments:
In `@workers/executor/executors/legacy_executor.py`:
- Around line 1947-1994: The current _inject_context_retrieval_metrics method
performs an extra fs.read via FileUtils.get_fs_instance to time file reads,
causing redundant I/O; remove the file read and measurement logic and instead
skip injecting a synthetic "context_retrieval" metric when the plugin did not
provide it: check result.data[PSKeys.METRICS] and if no per-prompt
"context_retrieval" exists, simply return (optionally emit a debug log and do
not mutate data), leaving real timing to the cloud single_pass_extraction plugin
or to a future opt-in estimation flag; update the method to stop calling fs.read
and remove timing variables (start/elapsed) and the try/except block while
preserving use of PSKeys, result, and context for locating the code.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro
Run ID: 3272e3e8-8a8c-4604-9165-38115004e547
📒 Files selected for processing (3)
workers/executor/executors/legacy_executor.pyworkers/file_processing/structure_tool_task.pyworkers/tests/test_context_retrieval_metrics.py
✅ Files skipped from review due to trivial changes (1)
- workers/tests/test_context_retrieval_metrics.py
🚧 Files skipped from review as they are similar to previous changes (1)
- workers/file_processing/structure_tool_task.py
for more information, see https://pre-commit.ci
There was a problem hiding this comment.
Actionable comments posted: 2
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@workers/file_processing/structure_tool_task.py`:
- Around line 728-733: The except block logs metadata_path which is created only
inside the try, risking an UnboundLocalError; fix this by ensuring metadata_path
is defined before the try (e.g., initialize metadata_path = None or a sentinel
like "<unknown>" above the try) or use a safe fallback when logging (e.g., use
locals().get("metadata_path") or conditional formatting) so the logger.error
call in the except path (the call that currently references metadata_path and
exc_info=True) cannot raise a new exception.
- Around line 663-669: The code currently overwrites the original
input_file_path (logger.info + fs.json_dump for input_file_path) before writing
the copy in COPY_TO_FOLDER, which risks corrupting the original if the copy
write fails; change the operation order so you first create copy_to_folder (use
copy_to_folder, execution_data_dir), write the copy_output_path (fs.json_dump to
copy_output_path using stem and structured_output), verify that write succeeds,
and only then overwrite the original input_file_path (fs.json_dump for
input_file_path and associated logger.info); keep the same variables
(input_file_path, copy_output_path, stem, structured_output) so the change is
localized and atomic from the task's perspective.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro
Run ID: 4c36a1da-b7a1-4a94-bd2a-ee6bc310125c
📒 Files selected for processing (1)
workers/file_processing/structure_tool_task.py
Review NotesHIGH: Destination connector
|
…ming Drop _inject_context_retrieval_metrics and its call site in _handle_single_pass_extraction. The helper was timing a second fs.read against a warm cache (the cloud plugin had already read the file to build its combined prompt) and reporting that under context_retrieval, which is a fabricated number, not a measurement. The cloud plugin is the source of the file read for single-pass and is responsible for populating context_retrieval in its returned metrics. Updated the docstring to spell out the contract. Also fix misleading "Completed prompt" streaming in the table and line-item extraction wrappers: the message was firing on both the success and failure branches, and on failure the user never saw the error (it only went to logger.error). Move the success-only message into the success branch and stream the error at LogLevel.ERROR on the failure branch. Fall back to "unknown error" when the plugin returns an empty result.error. Drop the now-orphan TestInjectContextRetrievalMetrics test class (six tests calling the deleted method) and update the module docstring. Surviving classes (TestSinglePassChunkSizeForcing, TestPipelineIndexUsageKwargsPropagation) cover unrelated invariants and are kept. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
|
Frontend Lint Report (Biome)✅ All checks passed! No linting or formatting issues found. |
Test ResultsSummary
Runner Tests - Full Report
SDK1 Tests - Full Report
|


What
A collection of fixes and follow-ups on top of the v2 executor-worker architecture,
LegacyExecutorvia the new line-item executor plugin (PRs Feat/line item executor plugin #1899, Feat/line item executor plugin #1900).null(not the string"NA") when nothing is extractable, with the FE now rendering it as a plainnullcell that visually matches other prompt outputs.prompt_studio_helperinto theide_callbackworker so commits happen after the async run completes.COPY_TO_FOLDER/to match the old Docker tool layout that the destination connector expects.usage_kwargs(run_id, execution_id, file_name) into the indexing path so API deployment responses no longer drop the embedding entry.backend/backend/worker_celery.pyshim and the oldprompt_studio_core_v2/tasks.py(+ tests) are removed; bothprompt_studio_helper.pyandviews.pynow use the unifiedbackend.celery_service.app.FileStorageinstead of the local filesystem so it works on remote storage backends (fixesPermissionError).chunk-size=0so it uses full-context retrieval instead of vector DB retrieval.ManageLlmProfilesadd-vs-edit state reset,AddLlmProfileform reset on profile switch, ConfigureDs / Settings.css tweaks.Why
These are issues that surfaced after the v2 executor architecture was rolled into main:
celery_executor_legacywith everything else, so a slow agentic run could starve regular extractions. Adding a dedicatedcelery_executor_agenticqueue lets us scale and prioritize the two paths independently.LegacyExecutor._handle_outputs()raisedLegacyExecutorError("LINE_ITEM extraction is not supported."). With the line-item executor plugin landing, that branch now delegates to the plugin instead."NA"string leaking to the FE for empty email/date —_convert_scalar_answeronly checked the first LLM response for"NA"; if the second LLM call also returned"NA", the literal string was returned. The FE then displayed"NA"as a normal value, which is wrong (it should be a missing-value indicator). We now returnNonein both cases and the FE renders the literalnulltext in the same font as a normal value.@track_subscription_usage_if_availabledecorator onindex_documentandprompt_responderran on the request thread, but the actual execution is now async (executor worker). The tracking call needs to happen after the run completes, so it has been moved intoide_index_complete/ide_prompt_completecallback tasks.COPY_TO_FOLDER/— the old DockerToolExecutor._setup_for_run()created this directory and the FS destination connector reads from it. The Celery-native_execute_structure_tool_impland_run_agentic_extractionwere skipping it, so FS destinations had nothing to copy._handle_index_for_pipelinewasn't passingusage_kwargsto_handle_index, so the embedding adapter callback couldn't tag the row with the rightfile_execution_id, and the entry was missing from the response metadata.worker_celery.pywas a parallel Celery app instance to route to executor workers. Now that the main Django Celery app already has the executor queue routes, the second app is redundant and the shim adds maintenance burden.PermissionErroron remote storage — the helper assumed it could open the input file directly withopen(), which fails when the file lives on S3/MinIO/etc. Reading viaFileStorage.read()works on every backend.chunk-size=0the retrieval path used the configured chunk size and missed full-context.How
Backend
backend/backend/worker_celery.py: deleted (114 lines).backend/prompt_studio/prompt_studio_core_v2/tasks.py+test_tasks.py: deleted (767 lines). Async callbacks now live inworkers/ide_callback/.backend/prompt_studio/prompt_studio_core_v2/prompt_studio_helper.py:_get_dispatcher()now usesbackend.celery_service.appdirectly instead of importing a separate worker Celery app.@track_subscription_usage_if_availabledecorators fromindex_documentandprompt_responder(subscription usage is now tracked in the IDE callback worker).backend/prompt_studio/prompt_studio_core_v2/views.py: replacedget_worker_celery_app()withbackend.celery_service.appforAsyncResultpolling.backend/prompt_studio/prompt_studio_core_v2/static/select_choices.json: re-keyline_item→line-itemto match the prompt-studio output type contract.backend/workflow_manager/workflow_v2/workflow_helper.py: 4 dead lines removed.Workers / executor
workers/shared/enums/worker_enums_base.py: newQueueName.EXECUTOR_AGENTIC = "celery_executor_agentic".workers/shared/infrastructure/config/registry.py:WorkerType.EXECUTORconfig now hasadditional_queues=[QueueName.EXECUTOR_AGENTIC].workers/executor/worker.py: health endpoint now reports the actual subscribed queues fromCELERY_QUEUES_EXECUTORenv.workers/executor/executors/legacy_executor.py:_run_line_item_extraction()added; theLINE_ITEMbranch in_handle_outputs()now delegates to it instead of raising._handle_index_for_pipeline()accepts and forwardsusage_kwargsso embedding usage is tagged with run/execution/file IDs._convert_scalar_answer()returnsNonewhen either the first or second LLM call returns"NA"(case-insensitive, whitespace-trimmed)._sanitize_null_values/_sanitize_dict_valuesuse.strip().lower()so whitespace-padded"NA"is also normalized toNone.chunk-size=0andchunk-overlap=0on each output so retrieval uses the full extracted text.workers/file_processing/structure_tool_task.py:_execute_structure_tool_impland_run_agentic_extractionwrite the structured output JSON into{execution_data_dir}/COPY_TO_FOLDER/{stem}.jsonin addition to overwriting INFILE.exc_info=Truefor diagnosability.workers/ide_callback/tasks.py: new_track_subscription_usage()helper called from bothide_index_completeandide_prompt_complete. Errors are logged but never fail the callback.workers/shared/workflow/destination_connector.py:RuntimeErrorwhentool_execution_resultis missing and there's no recorded execution error (previously logged + continued silently).metadata_file_path/INFILEfor diagnosability.workers/shared/workflow/execution/service.py:source_file_namesimplified to justfile_nameinexecute_structure_toolparams (was unnecessarily computingbasename).workers/run-worker-docker.sh: registerside-callbackworker type →ide_callbackqueue → health port8089.Frontend
DisplayPromptResult.jsx: split the previousoutput === undefined || output === nullguard.undefinedstill renders the "Yet to run" indicator;nullnow renders a plain<Typography.Text className="prompt-output-result">null</Typography.Text>so the font, size, weight and color match all other prompt outputs (no italic, no light grey, no oversize).PromptCard.css: removed the.prompt-null-valuerule that was making thenulltext italic / grey / 13px.DisplayPromptResult.test.jsx: updated the null-output test to assert the literal"null"text and that the "Yet to run" indicator is not shown.ManageLlmProfiles.jsx: cleareditLlmProfileIdwhen "Add new LLM profile" is clicked, so the modal opens in add mode instead of inheriting the previously-edited profile.AddLlmProfile.jsx: drop the unmount-timesetEditLlmProfileId(null)(handled by the parent now). When the form needs to reset, useform.setFieldsValue(formDetails)instead ofform.resetFields()so the prefilled defaults are kept.ConfigureDs.jsx: minor.Settings.css: minor cleanup (1 line removed).Can this PR break any existing features. If yes, please list possible items. If no, please explain why. (PS: Admins do not merge the PR without this section filled)
No, these are fixes for async execution
Database Migrations
Env Config
NA
Relevant Docs
NA
Related Issues or PRs
worker_celery.pyand fix ide-callback workerDependencies Versions
Notes on Testing
celery_executor_agentic(RabbitMQ UI) and is picked up by the executor worker.line-itemagainst a multi-row document.Emailprompt against a document with no email address. Run aDateprompt against a document with no date.nullin the same font, size, weight and color as a successful text extraction in a sibling prompt — no italic, no light grey, no oversized text.worker-ide-callbackworker (check its logs forIDE subscription usage committed for run_id=…).{file_execution_dir}/COPY_TO_FOLDER/{stem}.jsonexists and contains the structured output.embeddingentry should now appear in the metadata for that file.FILE_STORAGE_PROVIDER=minio(ors3) and run a workflow that uses the Unstructured IO X2Text adapter.PermissionError.Screenshots
N/A — the only visible change is that the rendered
nulltext in prompt-output cells now matches the styling of other prompt values (same font / size / weight / color).Checklist
I have read and understood the Contribution Guidelines.