Skip to content

UN-3142 [FEAT] Trace an execution to its LLMWhisperer call - #2246

Open
Deepak-Kesavan wants to merge 1 commit into
mainfrom
UN-3142-whisper-hash-tracing
Open

UN-3142 [FEAT] Trace an execution to its LLMWhisperer call#2246
Deepak-Kesavan wants to merge 1 commit into
mainfrom
UN-3142-whisper-hash-tracing

Conversation

@Deepak-Kesavan

Copy link
Copy Markdown
Contributor

What

Makes a workflow execution traceable to the LLMWhisperer call behind it.

  • Sends the real source filename to LLMWhisperer, so its reports identify the document.
  • Captures the whisper hash for every extraction that returns one (previously only when highlighting was enabled) and surfaces it in the customer-facing execution log.
  • Fixes a data-loss bug in the METADATA.json write that this change would otherwise have made far more frequent.

Why

Raised by a customer (via Abhishek Pathak) in UN-3142: there is no way to connect an execution to an LLMWhisperer call. Two concrete gaps caused this:

  1. No usable filename. The document is streamed to LLMWhisperer from the execution's internal copy (INFILEexecution_file_handler.py), and the client's filename param was never set. LLMWhisperer's reports therefore had nothing identifying the source document.
  2. The whisper hash was effectively invisible. It identifies the call, but was only recorded when enable_highlight was true, and otherwise only reached service logs — which customers cannot see.

How

Filename → LLMWhisperer

  • WhispererRequestParams gains a filename field; get_whisperer_params emits it as the client's filename query param (supported by llmwhisperer-client ≥ 2.6.2).
  • X2Text.process injects it from usage_kwargs[file_name], which every caller already populates for usage auditing — so this works at all call sites without touching each one. Callers can override with an explicit file_name kwarg; the agentic path in structure_tool_task.py does exactly that, since it does not build usage_kwargs.

Whisper hash → customer-facing log

  • LegacyExecutor._handle_extract now reads the hash after both the highlight and non-highlight branches, records it whenever one is returned, and appends it to the Text extraction completed log streamed to the user.

METADATA.json data loss (found during self-review)

  • _update_exec_metadata used ToolUtils.dump_json, a blind full overwrite, writing only {"whisper-hash": ...}. METADATA.json is shared — source_name, source_hash, tags and timing are written before extraction and tool results after — so that write destroyed them. Today it only fires when highlighting is on; capturing unconditionally would have made it fire on every tool-source extraction. It now merges into the existing file, matching what _write_tool_result already does.
  • Also guarded a missing execution_data_dir, which would previously raise on Path(None).

Deliberately not changed: the LLMWhisperer tag. It currently carries the customer's workflow tags, and repurposing it to hold execution ids would regress anyone relying on tag-based reporting. Tagging the call with execution details (bullet 2 of the ticket) needs LLMWhisperer-side support for multiple tags or a dedicated metadata field — see "Related Issues".

Can this PR break any existing features. If yes, please list possible items. If no, please explain why.

Low risk, but three behaviour changes worth naming:

  1. filename is now sent to LLMWhisperer. It is an optional param the client already supports and defaults to "" when the name is unknown, so behaviour is unchanged when no name is available. It carries the customer's own document name to a service that is already receiving the document itself, so this exposes nothing new.
  2. The metadata write now runs on non-highlighted extractions too. This is the intended fix. It is guarded on a hash actually being returned, so non-LLMWhisperer extractors (which return none) write nothing — verified by test.
  3. _update_exec_metadata merges instead of overwriting. Strictly recovers data that was previously being dropped; nothing reads METADATA.json expecting it to contain only the whisper hash (destination_connector.py reads whisper-hash by key).

The tag path is untouched, and a test pins that filename injection does not disturb it.

Database Migrations

  • None.

Env Config

  • None.

Relevant Docs

  • LLMWhisperer text extraction API — filename, tag params.

Related Issues or PRs

This covers bullets 1 and 3 of the ticket (log the details in customer-facing logs; send the correct file name). Bullet 2 — tagging the whisper call with execution details — is deliberately deferred, as it cannot be done well without LLMWhisperer-side support for more than one tag. The whisper hash now exposed in the logs provides the correlation the customer asked for without requiring changes to both products.

Dependencies Versions

  • No changes. Uses filename, already supported by the pinned llmwhisperer-client>=2.6.2.

Notes on Testing

Automated — full suites green locally:

  • workers: 1329 passed, 141 skipped
  • unstract/sdk1: 527 passed (+7 new)

New coverage:

  • unstract/sdk1/tests/test_x2text_source_filename.py (7 tests) — filename injected from usage_kwargs; explicit kwarg wins; absent name does not raise; adapter forwards it into WhispererRequestParams; emitted as the filename query param; defaults to empty; tag behaviour unchanged.
  • workers/tests/test_legacy_executor_extract.py (7 new) — hash captured with highlight disabled; extractors returning no hash write nothing; missing execution_data_dir does not crash; hash present in the customer-facing log; completion log clean when there is no hash; pre-existing METADATA.json keys preserved; corrupt METADATA.json does not fail the extraction.

Not yet verified live: that the deployed LLMWhisperer API surfaces the filename we now send in its reports. The param is accepted by the pinned client and is optional, so the risk is that it is ignored, not that extraction breaks. Worth confirming against a dev-namespace run before this is treated as closing the customer's request.

Screenshots

N/A — no UI surface; the change appears as an extra detail on the existing Text extraction completed execution log line.

Checklist

I have read and understood the Contribution Guidelines.

Customers had no way to connect a workflow execution to the LLMWhisperer
call behind it. Two gaps caused this:

- The document is streamed to LLMWhisperer from the execution's internal
  copy (INFILE), and the client's `filename` param was never set, so
  LLMWhisperer's reports had nothing identifying the source document.
- The whisper hash, which identifies the call, was only recorded when
  highlighting was enabled, and only ever reached service logs.

Changes:
- Forward the source filename to LLMWhisperer. `X2Text.process` injects it
  from `usage_kwargs`, which every caller already populates; callers may
  override with an explicit `file_name` kwarg.
- Capture the whisper hash for every extraction that returns one, not just
  highlighted ones, and report it in the customer-facing execution log.
- Preserve the rest of METADATA.json when recording the hash. The write was
  a blind overwrite that dropped source name, source hash and timing; now
  that it runs on every extraction rather than only highlighted ones, it
  merges instead.
- Guard against a missing execution data dir, which would previously raise
  on `Path(None)`.

The LLMWhisperer `tag` is deliberately unchanged — it carries customer
workflow tags, and repurposing it for execution ids needs LLMWhisperer-side
support for multiple tags.
@sonarqubecloud

Copy link
Copy Markdown

@greptile-apps

greptile-apps Bot commented Aug 17, 2026

Copy link
Copy Markdown
Contributor

Greptile Summary

This PR forwards source filenames to LLMWhisperer V2 and records returned whisper hashes in legacy executor metadata and customer-facing logs while preserving existing METADATA.json fields. The direct agentic extraction path receives the filename improvement but still drops the returned whisper hash.

  • Adds filename propagation through X2Text and the LLMWhisperer V2 request DTO.
  • Captures and logs whisper hashes for legacy executor extractions regardless of highlighting.
  • Changes whisper-hash metadata persistence from overwrite to read/merge/write.
  • Adds focused SDK and worker tests for filename forwarding, hash logging, and metadata preservation.

Confidence Score: 4/5

The agentic extraction path should capture and expose its returned whisper hash before this PR is merged, otherwise the advertised execution-to-call tracing remains incomplete.

Legacy executor extractions gain the intended correlation metadata, but current agentic studio executions bypass that handler and discard the hash returned by their direct X2Text call.

Files Needing Attention: workers/file_processing/structure_tool_task.py

Important Files Changed

Filename Overview
unstract/sdk1/src/unstract/sdk1/x2txt.py Injects the source filename from usage metadata while preserving an explicitly supplied filename.
unstract/sdk1/src/unstract/sdk1/adapters/x2text/llm_whisperer_v2/src/helper.py Adds the filename parameter to LLMWhisperer V2 requests with an empty-string fallback.
workers/executor/executors/legacy_executor.py Captures hashes across highlighted and non-highlighted legacy extraction paths and merges them into shared metadata.
workers/file_processing/structure_tool_task.py Forwards the agentic source filename but discards the extraction metadata, leaving whisper-hash tracing incomplete for this path.
unstract/sdk1/tests/test_x2text_source_filename.py Covers filename precedence and propagation through the V2 request stack.
workers/tests/test_legacy_executor_extract.py Covers legacy executor hash persistence, logging, missing directories, and preservation of existing metadata.

Sequence Diagram

sequenceDiagram
    participant Workflow as Agentic workflow
    participant Task as _run_agentic_extraction
    participant X2T as X2Text / LLMWhisperer
    participant Metadata as METADATA.json / execution log
    Workflow->>Task: Run agentic extraction
    Task->>X2T: "process(file_name=source_file_name)"
    X2T-->>Task: TextExtractionResult(text, whisper_hash)
    Task->>Task: Consume extracted_text only
    Note over Task,Metadata: whisper_hash is not persisted or logged
    Task-->>Workflow: Structured extraction result
Loading

Fix all with Greploop

Fix All in Claude Code

Prompt To Fix All With AI
### Issue 1
workers/file_processing/structure_tool_task.py:725-730
**Agentic path drops whisper hash**

When an agentic studio tool extracts a document through `_run_agentic_extraction`, this direct `X2Text` call consumes only `extracted_text` and discards `extraction_metadata.whisper_hash`, causing the hash to be absent from `METADATA.json` and customer-facing execution logs.

---

For each issue above, determine whether it is valid and should be fixed. If so, fix it directly.

Reviews (1): Last reviewed commit: "UN-3142 [FEAT] Trace an execution to its..." | Re-trigger Greptile

Comment on lines 725 to 730
extraction_result = x2text.process(
input_file_path=input_file_path,
enable_highlight=enable_highlight,
file_name=source_file_name,
fs=fs,
)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

P1 Agentic path drops whisper hash

When an agentic studio tool extracts a document through _run_agentic_extraction, this direct X2Text call consumes only extracted_text and discards extraction_metadata.whisper_hash, causing the hash to be absent from METADATA.json and customer-facing execution logs.

Knowledge Base Used: Workers (Celery) Service

Prompt To Fix With AI
This is a comment left during a code review.
Path: workers/file_processing/structure_tool_task.py
Line: 725-730

Comment:
**Agentic path drops whisper hash**

When an agentic studio tool extracts a document through `_run_agentic_extraction`, this direct `X2Text` call consumes only `extracted_text` and discards `extraction_metadata.whisper_hash`, causing the hash to be absent from `METADATA.json` and customer-facing execution logs.

**Knowledge Base Used:** [Workers (Celery) Service](https://app.greptile.com/zipstack/-/custom-context/knowledge-base/zipstack/unstract/-/docs/workers-celery.md)

---

For each issue above, determine whether it is valid and should be fixed. If so, fix it directly.

Fix in Claude Code

@github-actions

Copy link
Copy Markdown
Contributor

Unstract test results

Per-group results

Status Group Tier Passed Failed Errors Skipped Duration (s)
e2e-api-deployment e2e 3 0 0 0 20.4
e2e-coowners e2e 1 0 0 0 1.4
e2e-etl e2e 1 0 0 0 8.4
e2e-login e2e 2 0 0 0 1.4
e2e-prompt-studio e2e 1 0 0 0 4.6
e2e-smoke e2e 2 0 0 0 1.2
e2e-workflow e2e 1 0 0 0 16.7
integration-backend integration 267 0 0 26 45.3
integration-connectors integration 1 0 0 7 7.9
integration-workers integration 140 0 0 1 48.6
unit-backend unit 998 0 0 1 41.3
unit-connectors unit 63 0 0 0 10.3
unit-core unit 33 0 0 0 1.4
unit-platform-service unit 15 0 0 0 2.8
unit-rig unit 117 0 0 0 5.5
unit-sdk1 unit 525 0 0 0 35.6
unit-workers unit 1353 0 0 1 104.2
TOTAL 3523 0 0 36 356.9

Critical paths

⚠️ Critical paths not yet covered

  • workflow-execution-fan-out — Multi-file workflow execution fans out to file-processing workers and rejoins. (declared coverage: no groups declared)
✅ Covered critical paths
  • auth-login — covered by e2e-login
  • adapter-register-llm — covered by integration-backend
  • workflow-author — covered by integration-backend
  • co-owner-manage — covered by integration-backend, e2e-coowners
  • workflow-create-execute — covered by e2e-workflow
  • api-deployment-provision — covered by integration-backend
  • api-deployment-auth — covered by integration-backend
  • api-deployment-run — covered by e2e-api-deployment
  • mcp-server-auth — covered by integration-backend
  • mcp-platform-auth — covered by integration-backend
  • prompt-studio-author — covered by integration-backend
  • prompt-studio-fetch-response — covered by e2e-prompt-studio
  • connector-register-test — covered by integration-backend
  • pipeline-etl-execute — covered by e2e-etl
  • usage-aggregate-read — covered by integration-backend
  • usage-token-tracking — covered by e2e-api-deployment
  • callback-result-delivery — covered by e2e-api-deployment

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.

1 participant