Skip to content

feat(ai-smart-file-tagger-qa): add AI Smart File Tagger#490

Open
LukasHirt wants to merge 16 commits into
mainfrom
ext/2026-06-24-ai-smart-file-tagger-qa
Open

feat(ai-smart-file-tagger-qa): add AI Smart File Tagger#490
LukasHirt wants to merge 16 commits into
mainfrom
ext/2026-06-24-ai-smart-file-tagger-qa

Conversation

@LukasHirt

Copy link
Copy Markdown
Collaborator

AI-generated · OSPO-50 · Gate: ✅ 1.00

Problem

Files accumulate without tags, making search and filter ineffective.
Manual tagging at scale doesn't happen — it requires opening every file individually.

Solution

A sparkle icon appears inline in each file row. Clicking it sends the file's
content (txt/md/pdf) or just its name and type (all others) to the configured
LLM, which returns 3–5 suggested tags shown in a lightweight confirmation
popover. The user accepts, deselects, or dismisses. With structured-output
models, tags include a confidence score; with basic text models, a flat list.
Without a configured LLM, the button shows a tooltip describing the required
admin setup.

Extension points

app.files.quick-actions

Why ship this now

As oCIS deployments scale, findability suffers first. This is the lowest-friction
path to enriched metadata, meeting users exactly where they already are.

What was built

packages/web-app-ai-smart-file-tagger-qa adds an inline "suggest tags" action to the files app, sending a file's content or name/MIME type to a configured LLM and returning suggested tags for the user to accept or dismiss. Because app.files.quick-actions doesn't exist in the installed @ownclouders/web-pkg (v12.3.2), the action is registered on global.files.context-actions instead — accessible from the file's context menu rather than as an inline row icon. src/index.ts reads applicationConfig.llm, builds the LLM config, and wires the ActionExtension registration.

The core logic lives in src/composables/useTagSuggestions.ts, which pulls file content over WebDAV for .txt/.md/.pdf (PDF text extraction via pdfjs-dist with a fake worker) and falls back to filename + MIME type for everything else, gated by src/utils/file-support.ts. It calls the existing useLLM.complete() composable with a structured JSON prompt and parses a { tags: [{ name, confidence }] } response, with a plain-text comma/newline fallback for models that don't support structured output — matching the two-tier degradation already established by web-app-ai-doc-summary. Accepted tags are applied via clientService.graphAuthenticated.tags.assignTags(). src/components/TagSuggestionModal.vue renders the suggestion UI: a loading spinner, an unconfigured-LLM notice, toggleable OcTag chips with confidence badges (selection state is tracked via a CSS class since this OcTag version has no native selected prop), an error banner, and its own Apply/Dismiss controls since the modal is launched with hideConfirmButton: true.

Test coverage includes 36 unit tests across the composable (prompt construction, structured/plain-text parsing, confidence normalization, tag application, and 401/429/network error handling) and the modal component (state rendering, chip toggling, confirm/dismiss behavior), plus a Playwright acceptance spec that mocks the LLM proxy, uploads a test file, triggers the action from the context menu, and verifies the modal's chip rendering, toggle, and confirm-close flow. The acceptance spec and its page object live inside the package rather than shared support/pages/, per the per-extension page-object convention. A known limitation, documented in the package README, is that tag suggestions surface from the file context menu rather than as an inline quick-action icon, since the originally spec'd extension point isn't available in this web-pkg version.

Gate

Check Result
Hygiene ✅ ok
Build ✅ ok
Lint ✅ ok
Unit tests ✅ ok
E2E tests ✅ ok
Score 1.00

Effort: S · 🤖 Generated by extctl

@kw-security

kw-security commented Jun 30, 2026

Copy link
Copy Markdown

Snyk checks have passed. No issues have been found so far.

Status Scan Engine Critical High Medium Low Total (0)
Open Source Security 0 0 0 0 0 issues
Licenses 0 0 0 0 0 issues

💻 Catch issues earlier using the plugins for VS Code, JetBrains IDEs, Visual Studio, and Eclipse.

@LukasHirt LukasHirt requested a review from dj4oC June 30, 2026 21:45
@LukasHirt LukasHirt self-assigned this Jun 30, 2026
@LukasHirt LukasHirt force-pushed the ext/2026-06-24-ai-smart-file-tagger-qa branch from 8c0104a to 6d1c358 Compare June 30, 2026 21:47

@dj4oC dj4oC left a comment

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.

Adds a context-menu "Suggest tags" action for single files that calls a same-origin ai-llm-proxy, renders chip selection in a modal, and applies confirmed tags via assignTags (Graph API).

  • Security: no findings — same-origin enforcement correct (CWE-918 gated), bearer token protected, tag normalization (lowercase, 30-char cap) appropriate ✅
  • Stability: no findings — assignTags merge into local store rather than overwrite is correct ✅
  • Performance: PDF.js client-side extraction is scoped and capped (12,000 chars); no unbounded allocation ✅
  • Test coverage: unit + e2e tests present ✅
  • TODOs found: 1 — "description": "CANDIDATE" in package.json (tracking issue to follow)
  • Dependency: pdfjs-dist@^6.1.200 (Apache-2.0) added — covered by existing dependabot.yml npm config ✅
  • CI status: all green per Snyk ✅ (build verification not possible in cloud review environment)
  • Stale review: ⚠️ This PR has been awaiting review since 2026-06-30 — exceeded 48h threshold by 8 days

No blocking findings. This can proceed to human sign-off once the package.json description placeholder is resolved (see tracking issue).

🤖 Automated review by Claude Code (security · stability · performance · coverage)

@dj4oC

dj4oC commented Jul 8, 2026

Copy link
Copy Markdown
Contributor

TODO tracking: issue #501 opened to replace the "description": "CANDIDATE" placeholder in package.json before publish.

@dj4oC

dj4oC commented Jul 9, 2026

Copy link
Copy Markdown
Contributor

@copilot resolve the merge conflicts on this branch.

Copilot AI requested a review from a team as a code owner July 9, 2026 22:12

Copilot AI commented Jul 9, 2026

Copy link
Copy Markdown

@copilot resolve the merge conflicts on this branch.

Addressed in 45e96c7. The branch now includes the merge conflict resolution against main.

Copilot finished work on behalf of dj4oC July 9, 2026 22:15
Copilot AI requested a review from dj4oC July 9, 2026 22:15
LukasHirt and others added 16 commits July 14, 2026 19:57
Signed-off-by: Lukas Hirt <info@hirt.cz>
…mposables/useTagSuggestions.ts` (prompt construction, LLM fetch, JSON parse with confidence scores, plain-text fallback, WebDAV tag write via `useClientService`), `src/utils/file-support.ts` (content-extraction eligibility check, MIME helper), and wire `src/index.ts` to read `applicationConfig.llm`, build `llmConfig`, and register the `ActionExtension` on `app.files.quick-actions` (with fallback to `global.files.context-actions` if the extension point is absent). Verify the quick-actions extension point exists in `node_modules/@ownclouders/web-pkg` and confirm an unused dev-server port in the `97XX` range.

Signed-off-by: Lukas Hirt <info@hirt.cz>
Signed-off-by: Lukas Hirt <info@hirt.cz>
Signed-off-by: Lukas Hirt <info@hirt.cz>
…ts/TagSuggestionModal.vue`: loading spinner, selectable tag chips (OcTag or OcButton toggle fallback), confidence badges, error banner, confirm/dismiss buttons, and unconfigured-state info message. Wire the modal into the action handler via `useModals().dispatchModal`. Use only ODS components and ownCloud CSS custom properties; wrap all strings in `$pgettext`.

Signed-off-by: Lukas Hirt <info@hirt.cz>
…/composables/useTagSuggestions.spec.ts` covering prompt construction (content mode vs name-only mode), structured JSON parse, plain-text fallback, tag application via mocked WebDAV, and error branches (401, 429, network failure). Write `tests/unit/components/TagSuggestionModal.spec.ts` covering loading state, chip rendering, chip toggle, confirm calls `applyTags`, dismiss closes, error banner, and unconfigured tooltip.

Signed-off-by: Lukas Hirt <info@hirt.cz>
Signed-off-by: Lukas Hirt <info@hirt.cz>
Signed-off-by: Lukas Hirt <info@hirt.cz>
…on in `tests/e2e/acceptance.spec.ts` with a Playwright smoke test: upload a `.txt` file, click the sparkle quick-action, assert the `TagSuggestionModal` appears with at least one chip, toggle a chip off, confirm, and assert the modal closes.

Signed-off-by: Lukas Hirt <info@hirt.cz>
Signed-off-by: Lukas Hirt <info@hirt.cz>
Signed-off-by: Lukas Hirt <info@hirt.cz>
Signed-off-by: Lukas Hirt <info@hirt.cz>
… state sync issues

- Fix tag chips rendering as raw JSON/numbered-list text when the LLM
  ignores the requested response format
- Fix selected/unselected chip colors having inverted contrast; tags now
  default to unselected (opt-in) instead of pre-selected
- Replace the modal's custom Dismiss/Apply tags button row with the
  framework's own modal actions (confirmText + exposed onConfirm)
- Add a hint above the chip list explaining the tags must be selected
- Sync the resources store after applying tags so the file list/sidebar
  reflect the new tags without a page refresh
- Fix a race in the e2e test that read the chip count before the async
  suggestion fetch resolved

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Signed-off-by: Lukas Hirt <info@hirt.cz>
Signed-off-by: Lukas Hirt <info@hirt.cz>
…I matrix, and oCIS apps config

Signed-off-by: Lukas Hirt <info@hirt.cz>
…laceholder description

Bumps @ownclouders/web-client, @ownclouders/web-pkg, and
@ownclouders/extension-sdk to 12.5.0 to match the rest of the monorepo, and
replaces the "CANDIDATE" package.json description with a real one.

Closes #501

Signed-off-by: Lukas Hirt <info@hirt.cz>
@LukasHirt LukasHirt force-pushed the ext/2026-06-24-ai-smart-file-tagger-qa branch from 45e96c7 to c35f7da Compare July 14, 2026 18:01
@LukasHirt

Copy link
Copy Markdown
Collaborator Author

Branch was stale — rebased onto origin/main to resolve conflicts with the newly-merged web-app-ai-multi-doc-synthesizer package (PR #466), which touched the same shared config files (docker-compose.yml, dev/docker/ocis.apps.yaml, support/actions/ocis.apps.yaml, .github/workflows/test.yml). All conflicts were additive (both packages appending their own entries) and resolved by keeping both.

Also addressed the review's open item:

Verified post-rebase: check:types, build, lint, and test:unit (39 tests) all pass for this package.

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.

web-app-ai-smart-file-tagger-qa: replace placeholder package description

4 participants