8000 feat(documents): read documents with an Apache Tika server by adeelahmad · Pull Request #292 · adeelahmad/lense · GitHub
[go: up one dir, main page]

Skip to content

feat(documents): read documents with an Apache Tika server - #292

Merged
adeelahmad merged 10 commits into
mainfrom
claude/project-thread-7jifa3
Oct 14, 2026
Merged

adeelahmad merged 10 commits into
mainfrom
claude/project-thread-7jifa3

Conversation

@adeelahmad
Copy link
Copy Markdown
Owner

Requested by Adeel · project thread

Description

Before: a Word, PowerPoint or spreadsheet file on a lean server or a Raspberry Pi (no LibreOffice, no anytopdf node) was refused, and Lens knew nothing of a document's own title, authors or dates.

After: Lens can use an Apache Tika server. The compose files get an opt-in tika profile (docker compose --profile tika up -d, or COMPOSE_PROFILES=tika). In Settings → Documents, one button fills in http://tika:9998, and a Test button checks it. Then:

  • Fallback reader (documents.tika_text, on): when LibreOffice, Chromium and anytopdf can't make a document into a PDF here, or one of them fails on it, Tika reads it as text with no pages. The text is searched, analyzed and asked about like any other. The engine is text:tika, and uploads accept Office files.
  • Self-description (documents.tika_metadata, on): every document is described by Tika as Dublin Core terms (described on the resource: title, creators, subject, language, created, modified, pages, words, format). The title and creation date replace the file-name defaults. The authors and language flow into the resource's metadata, and from there into RDF and schema.org. The Details tab shows them. If Tika is down, the document is still read.

Off until documents.tika_url is set, so nothing changes by default.

How: domain/tika.py calls PUT /rmeta/text (Tika 4.x tk: keys and the 2.x/3.x X-TIKA: keys) and GET /version. Requests go only to the address the admin set: http(s), no credentials, no redirects. The file is sent as document.<ext>. Answers are capped at 64 MB and text at 20M characters. convert.unavailable lets Tika cover what nothing else can, and documents.transcribe falls back to it. The compose service runs apache/tika:4.0.0-1, read-only, as a non-root user, with no published port and a 512 MB heap (TIKA_HEAP, TIKA_VERSION).

Motivation and Context

Adeel asked to integrate and set up Apache Tika: text and metadata for many formats, alongside anytopdf.

Steps to reproduce

Tested against a real apache/tika:4.0.0-1 container (LENS_TEST_TIKA=http://127.0.0.1:9998 pytest tests/api/test_tika.py). The image starts read-only in about 1 s and uses about 220 MB.

Types of changes

  • New feature (non-breaking change which adds functionality)

Checklist:

  • The title is a Conventional Commit.
  • My code follows the code style of this project.
  • I added or updated tests (tests/api/test_tika.py with a fake Tika server, plus a real-server test; frontend describedRows).
  • Docs updated (configuration.md#apache-tika, api.md, deployment.md).
  • CHANGELOG under Unreleased.

Done questions

  1. Goal delivered and merged green? Merge follows once CI is green.
  2. Agents can use it? Yes: settings API, POST /settings/documents/tika/test, and described on GET /resources/{id}.
  3. Zero setup after install line? Off by default. Turning it on takes the profile plus one button in Settings.
  4. Fewest clicks? One button fills the address, then Save.
  5. Every setting in web app with default? Yes: tika_url (none), tika_text (on), tika_metadata (on). None are in the wizard.
  6. Light on a Pi? Yes, it's opt-in. The service is about 220 MB of RAM, and the docs say to leave it off on small Pis.
  7. Private/threat model? P-TIKA-SERVER added (md, yaml, json, validator passes). The compose service is reachable only inside Lens's network, and the docs warn that a remote server sees every document.
  8. Cheapest path? No LLM or ledger involved. Tika is only called when it is configured.
  9. Defaults unchanged? Yes: off until a URL is set.
  10. Safe upgrades? N/a for migration: described is an additive, schemaless field. Older documents get it when they are re-transcribed.
  11. Honest/labelled? Documents read this way say "read as text by Tika" and have no pages.
  12. Tested, documented, generated files via tooling? Yes: make openapi, 779 backend and 728 frontend tests passed locally.
  13. Open standards, MIT-compatible deps? Dublin Core terms. Tika is Apache-2.0 and runs as a separate service.
  14. Refine later: emails and archives read by Tika don't keep their attachments as files or resources. More formats Tika reads (epub, Pages, Keynote, vsdx) could be accepted when Tika is set. OCR needs the -full image.

🤖 Generated with Claude Code

https://claude.ai/code/session_01X6p3skuCAfa2nTxSmDKvxq


Generated by Claude Code

An opt-in Tika server (`--profile tika` in the compose files, or any Tika
server set in Settings → Documents) reads, as text, the documents nothing
here can make into a PDF, and what every document says about itself as
Dublin Core terms (`described` on the resource; its authors and language
feed the resource's metadata). Off until documents.tika_url is set.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01X6p3skuCAfa2nTxSmDKvxq
@adeelahmad adeelahmad self-assigned this Oct 10, 2026
…talled

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01X6p3skuCAfa2nTxSmDKvxq
Copy link
Copy Markdown
Owner Author

Blocked on merge conflicts with main in CHANGELOG.md, docker-compose.yml, docker-compose.prod.yml and threat-model.yaml. Merging main in will also pick up the 0.7.0 schema version; if #310 hasn't landed yet, set nextjs-frontend/openapi.json's info.version to 0.7.0 so the schema check passes.


Generated by Claude Code

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01X6p3skuCAfa2nTxSmDKvxq
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01X6p3skuCAfa2nTxSmDKvxq
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01X6p3skuCAfa2nTxSmDKvxq
…older

The survey passed, but rm -rf failed with 'Directory not empty' while the
just-killed API was still writing, failing the job.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01X6p3skuCAfa2nTxSmDKvxq
Copy link
Copy Markdown
Owner Author

The "Clicks and keystrokes per feature" check failed after every journey passed. The cause was the job's cleanup step: rm -rf on the temporary data folder failed with "Directory not empty" because the API it had just killed was still writing to that folder. This isn't caused by this PR's code; the check came in from main (#298). I put a fix in this PR in 944ace7: ci/clicks/run.sh now waits for the servers to exit before it removes the folder, and retries once.


Generated by Claude Code

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01X6p3skuCAfa2nTxSmDKvxq
…able converters

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01X6p3skuCAfa2nTxSmDKvxq
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01X6p3skuCAfa2nTxSmDKvxq
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01X6p3skuCAfa2nTxSmDKvxq
@adeelahmad
adeelahmad merged commit 52b645d into main Oct 14, 2026
18 checks passed
@adeelahmad
adeelahmad deleted the claude/project-thread-7jifa3 branch October 14, 2026 17:05
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

0