Skip to content

UN-3987 [FIX] Send LLMWhisperer V2 params under the correct names - #2236

Open
chandrasekharan-zipstack wants to merge 8 commits into
mainfrom
UN-3987-fix-line-splitter-strategy-key
Open

UN-3987 [FIX] Send LLMWhisperer V2 params under the correct names#2236
chandrasekharan-zipstack wants to merge 8 commits into
mainfrom
UN-3987-fix-line-splitter-strategy-key

Conversation

@chandrasekharan-zipstack

@chandrasekharan-zipstack chandrasekharan-zipstack commented Aug 11, 2026

Copy link
Copy Markdown
Contributor

What

The LLMWhisperer V2 adapter's line splitter strategy setting has never been applied. Two breaks stacked:

  1. Config key mismatch. The adapter's JSON schema stores the choice under line_splitter_strategy, but WhispererConfig.LINE_SPLITTER_STRATEGY was "line_spitter_strategy", so config.get() never matched and the left-priority default was always used.
  2. Wrong wire param. That same misspelled key was passed as a whisper() kwarg, so the client sent query param line_spitter_strategy, which the service does not read and Flask drops silently.

Fixing either alone changes nothing.

Changes

  • LINE_SPLITTER_STRATEGY is now "line_splitter_strategy" — matches the schema key and the wire param.
  • WhispererConfig gains PAGE_SEPARATOR_PARAM and FILE_NAME for the client kwarg names. page_seperator stays as the config key (existing adapter configs in the DB are stored under it, no migration needed) but is now sent as page_separator.
  • file_name is now sent, set to the basename of the file being extracted. Previously the adapter never sent it, so every Unstract-originated row in LLMWhisperer usage reports showed the service default. Separate commit if you'd rather drop it.
  • llmwhisperer-client pinned to >=2.8.0.

Blocked on the client release

>=2.8.0 does not exist yet — it is the release that carries Zipstack/llm-whisperer-python-client#34, which adds the correctly-named line_splitter_strategy, page_separator and file_name kwargs. On an older client these kwargs raise TypeError, so this must not merge before that release. Draft until then. On un-drafting: confirm the actual version number and regenerate the lockfiles.

Ordering: unstract-llm-whisperer#721 (service accepts page_separator) → llm-whisperer-python-client#34 release → this PR.

Behaviour change to expect

The service validates the strategy and returns 400 for anything outside left-priority / right-priority / mid-priority. Previously an out-of-range value was silently ignored, so a bad stored value becomes a hard error once the value actually arrives.

Users who never touched the setting are unaffected — adapter, client and service all default to left-priority.

Verification

tests/test_llm_whisperer_v2_params.py — 2 tests, both pass: the strategy is read from the schema key, and the page separator is read from the legacy config key but emitted under the corrected kwarg.

Not verified end to end: the extraction-output change needs the client release and a run against a deployed service.

Jira: UN-3987, LW-406

🤖 Generated with Claude Code

https://claude.ai/code/session_01Dra3Xevzb5oYtMz9fhj8iG

The adapter read the line splitter strategy under `line_spitter_strategy`
while its JSON schema stores it as `line_splitter_strategy`, so the user's
choice never applied. The same misspelling was also passed to the client,
which forwarded a query param the service does not read.

The page separator keeps its misspelled config key since existing adapter
configs are stored under it, but is now sent under the client's corrected
kwarg. Both need llmwhisperer-client 2.8.0 or newer.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Dra3Xevzb5oYtMz9fhj8iG
Without it, usage reports record the service default for every
Unstract-originated extraction, leaving no way to cross reference a row
back to a document.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Dra3Xevzb5oYtMz9fhj8iG
@chandrasekharan-zipstack
chandrasekharan-zipstack force-pushed the UN-3987-fix-line-splitter-strategy-key branch from 151b688 to 8b74d6f Compare August 11, 2026 08:39
@chandrasekharan-zipstack
chandrasekharan-zipstack marked this pull request as ready for review August 12, 2026 11:46
@sonarqubecloud

Copy link
Copy Markdown

@greptile-apps

greptile-apps Bot commented Aug 12, 2026

Copy link
Copy Markdown
Contributor

Greptile Summary

The PR corrects LLMWhisperer V2 parameter names, validates line-splitter strategies, forwards page separators and filenames, upgrades the client, and regenerates affected locks. It also expands lock automation, but the recurrence fix does not traverse transitive local dependencies.

  • Aligns adapter config keys with supported LLMWhisperer client kwargs.
  • Pins and resolves llmwhisperer-client 2.8.1 across the affected projects.
  • Adds parameter-focused tests and expands automated lock generation.

Confidence Score: 4/5

The PR should not merge until lock automation recursively detects local dependency changes, because a future sdk1-only dependency update can again leave the connectors lock stale.

The current locks are synchronized, but the added recurrence protection only checks direct local dependencies and therefore does not keep every independently installed transitive consumer synchronized.

Files Needing Attention: docker/scripts/uv-lock-gen/uv-lock.sh

Important Files Changed

Filename Overview
docker/scripts/uv-lock-gen/uv-lock.sh Adds missing projects to lock automation, but direct-only dependency detection leaves transitive consumer locks susceptible to recurrence.
unstract/sdk1/pyproject.toml Raises the LLMWhisperer client requirement to the released 2.8.1 version.
unstract/sdk1/src/unstract/sdk1/adapters/x2text/llm_whisperer_v2/src/helper.py Sends corrected parameter names, validates strategies, and attaches the input basename.
unstract/sdk1/tests/test_llm_whisperer_v2_params.py Covers supported and invalid splitter strategies plus legacy page-separator configuration.
unstract/connectors/uv.lock Currently resolves llmwhisperer-client 2.8.1, while also demonstrating that this transitive consumer lock depends on sdk1 metadata.

Flowchart

%%{init: {'theme': 'neutral'}}%%
flowchart LR
  SDK["sdk1 dependency changes"] --> FS["filesystem directly depends on sdk1"]
  FS --> CONN["connectors transitively consumes sdk1"]
  SCRIPT["lock automation"] --> DIRECT["checks connectors + direct dependencies"]
  DIRECT --> FS
  DIRECT -. "does not recurse" .-> SDK
  CONN --> STALE["connectors lock may remain stale"]
Loading

Fix all with Greploop Fix All in Claude Code

Prompt To Fix All With AI
### Issue 1
docker/scripts/uv-lock-gen/uv-lock.sh:110-112
**Transitive lock updates remain stale**

When a future `unstract/sdk1` dependency changes while `unstract/connectors` and its direct `unstract-filesystem` dependency remain unchanged, `has_dependency_changes` stops after inspecting the direct dependency and skips the connectors lock, causing `uv lock --check` or frozen installation to fail against its stale sdk1 dependency graph.

---

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

Reviews (4): Last reviewed commit: "Merge branch 'main' into UN-3987-fix-lin..." | Re-trigger Greptile

Comment thread unstract/sdk1/pyproject.toml Outdated
…mers

The uv-lock bot only checks a directory's own pyproject and its immediate
local path dependencies, so an sdk1-only dependency bump left five lockfiles
pinning the pre-2.8.0 client with a stale `>=2.6.2` specifier. `uv sync
--locked` rejected those directories.

Adds sdk1, tool-registry and workflow-execution to the bot's directory list
so a future sdk1 bump relocks them without a manual pass.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01EVoa8BMCNnhdkATd7A9U4J
…service accepts

The setting has never reached the service, so stored values were never
validated and the JSON schema accepted free text. Now that the value is
actually sent, anything outside left-priority / mid-priority / right-priority
makes the service reject the extraction with a 400.

Adds the enum to the schema so the field renders as a dropdown, and falls back
to the default with a warning when an existing config holds an unsupported
value. The fallback preserves what those configs resolve to today, and the
warning surfaces how many are affected without decrypting stored metadata.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01EVoa8BMCNnhdkATd7A9U4J
2.8.1 sends the page separator under both the corrected and the legacy query
key, so the adapter no longer depends on the LLMWhisperer service being new
enough to read the corrected one.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01EVoa8BMCNnhdkATd7A9U4J
@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.5
e2e-etl e2e 1 0 0 0 8.3
e2e-login e2e 2 0 0 0 1.2
e2e-prompt-studio e2e 1 0 0 0 4.5
e2e-smoke e2e 2 0 0 0 1.1
e2e-workflow e2e 1 0 0 0 17.9
integration-backend integration 290 0 0 26 43.8
integration-connectors integration 1 0 0 7 7.7
integration-workers integration 140 0 0 1 47.7
unit-backend unit 1001 0 0 1 40.1
unit-connectors unit 63 0 0 0 10.1
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.6
unit-sdk1 unit 528 0 0 0 31.6
unit-workers unit 1346 0 0 1 105.7
TOTAL 3545 0 0 36 351.4

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

Comment thread docker/scripts/uv-lock-gen/uv-lock.sh
has_dependency_changes only walked one level of [tool.uv.sources], so a
change to unstract/sdk1 relocked unstract/filesystem (a direct dependant)
but not unstract/connectors, which reaches sdk1 through it. That is why
the root and connectors locks were already stale at llmwhisperer-client
2.7.0 before this branch existed.

Read the resolved graph out of each uv.lock instead: uv has already
computed the closure there, so no recursion is needed. The pyproject is
still read so a dependency added but not yet locked is not missed.

Against this branch's sdk1 change, the old walk skips '.' and
unstract/connectors; the new one selects both.

Also cover unstract/tool-sandbox and tool-sidecar, the two remaining
directories with a uv.lock that the bot never visited.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01EVoa8BMCNnhdkATd7A9U4J
@sonarqubecloud

Copy link
Copy Markdown

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.

2 participants