Skip to content

[AI-101] Add the Assistant dropdown to the work package description editor - #25770

Open
tangopium wants to merge 36 commits into
feature/66020-pr12-llm-modulefrom
feature/ai-101-ai-actions-dropdown
Open

tangopium wants to merge 36 commits into
feature/66020-pr12-llm-modulefrom
feature/ai-101-ai-actions-dropdown

Conversation

@tangopium

@tangopium tangopium commented Oct 2, 2026 •

Copy link
Copy Markdown
Collaborator

Ticket

https://community.openproject.org/wp/AI-101

What are you trying to accomplish?

Stacked on #25734, the last PR of the AI-3 stack. First cut of the editor side of the description assistant: an "Assistant" split button in the work package description editor offers the actions the AI-134 endpoints answer with, and picking one runs it through the AI-136 endpoints and puts the result into the editor.

  • CKEditor plugin OPAiActions for the full editor (paired build-repo PR: [AI-101] Add the AI actions dropdown to the full editor toolbar commonmark-ckeditor-build#131, this PR carries the rebuilt bundle). The plugin only renders the list and reports the selection; it adds nothing while the list is empty and does nothing on cores without the aiActions service. The button is a split button at the right end of the toolbar, outside CKEditor's overflow grouping, so it stays visible at every editor width: the main part shows the sparkle Octicon of the Artificial Intelligence administration section next to the name of the action that was run last (the first action until then) and runs that action; the arrow opens the list of all actions.
  • AiActionsService in the core frontend: resolves the list for an existing work package (/work_packages/:id/ai_text_transform_actions) or, for a new one, through its project and type. defaultAction picks the action the button shows: the one run last in this browser, kept in local storage, or the first offered one. Running an action creates a run, polls it with the after cursor until a terminal status, and replaces the editor content with the completed text through the editor model, so a single undo restores the previous text. The editor is read-only meanwhile; toasts report progress, success or the run's error message. Errors and cancellations never touch the text.
  • Llm::TextTransformGateway in modules/llm, registered with the text transform port on boot. It resolves :description_assistant through Llm::Runtime, streams the completion with the action's prompt as instructions and the submitted text as the user message, and maps the module's errors onto the port's reasons. This covers AI-137 ahead of its own ticket.
  • AI::TextTransforms::EchoGateway, enabled with OPENPROJECT_AI_TEXT_TRANSFORM_ECHO_GATEWAY=true outside production, streams the submitted text back so the whole flow can be exercised without a model.

Screenshots

The branch also merges dev ahead of the stack base, because the rebuilt bundle is a CKEditor 49 build and needs the core's CKEditor 49 integration (#25966); the stack's next forward-merge absorbs that. Only ckeditor.js and its map are rebuilt; the stylesheet keeps the refreshed theme trimmed in 43bf22d and the translations stay as committed.

What approach did you choose and why?

The plugin is deliberately thin so that iterating on the editor behaviour needs no bundle rebuild; everything dynamic lives in the core service behind the plugin context. Availability stays server-side: an empty list leaves the toolbar untouched, the frontend carries no flag or setting logic. The dropdown is appended to the toolbar element itself and keeps itself behind the overflow menu, since CKEditor's grouping measures the toolbar's last child; a maxWidth nudge makes the toolbar regroup when the dropdown appears and whenever the shown action, and with it the button width, changes. The label is capped at 12em so an admin-defined action name cannot push the button out of the toolbar.

Execution follows the polling contract from AI-136: adaptive 400 to 1000 ms interval, in-flight guard through await, stop on a terminal status, result built from the completed event rather than concatenated deltas. A client-side deadline of 200 s requests cancellation of a run that never finishes.

The result is applied with model.insertContent over the root range instead of editor.setData(), which would drop the undo history. Persisting still happens only through the regular save flow.

The port learns about the module's gateway through a factory block set by the module's engine, so core never references module constants and code reloading in development keeps resolving the current class. Without the module the port falls back to the null gateway.

Deliberately not in this first cut, all from the AI-101 spec: diff preview with a clean/diff toggle, accept and reject, a cancel control during the run, progressive rendering of deltas. Known follow-up for the AI-3 stack: RubyLLM sends the instructions as OpenAI's developer role because Llm::Session does not set openai_use_system_role; self-hosted servers may reject that.

Merge checklist

  • Added/updated tests
  • Added/updated documentation in Lookbook (patterns, previews, etc) - not applicable, CKEditor toolbar
  • Tested major browsers (Chrome, Firefox, Edge, ...) - Chrome through Playwright against a fake OpenAI server; Firefox and Edge open

Behind the existing flag ai_text_transform_actions and setting ai_text_transform_actions_enabled, plus the AI-3 flag llm_connection and setting llm_features_enabled for the real gateway.

On PullPreview only the two flags are on. To see the dropdown there, switch on the AI text transform actions and the LLM features in the administration and connect an LLM server with a chat model on the LLM connection page.

Registers an aiActions service in the plugin context that resolves the
available actions for the work package description editor, and ships the
rebuilt CKEditor bundle containing the Assistant dropdown, which stays
hidden while the action list is empty.

Part of the AI-101 editor integration.
https://community.openproject.org/work_packages/77784
The AI-100 backend now ships its action list as HAL collections under
API v3 (AI-134): one endpoint scoped to an existing work package and one
scoped to a project and type for unsaved work packages. The editor
service consumes those instead of the earlier plain JSON draft, resolving
the project and type from the work package's links. The dropdown plugin
in the CKEditor build is unaffected: it still only renders labels.

https://community.openproject.org/work_packages/77784
Until AI-137 wires the text transform gateway to the LLM connection, the
null gateway reports "not configured" and every action list is empty, so
the Assistant dropdown never shows up locally. Setting
OPENPROJECT_AI_TEXT_TRANSFORM_ECHO_GATEWAY=true outside production swaps
in a gateway that is always ready and streams the submitted text back,
which is enough to exercise the dropdown and, later, the run flow.

https://community.openproject.org/work_packages/77784
First cut of the execute flow. Selecting an action creates a run through
the text transform API, polls it with an adaptive interval until it
reaches a terminal status, and replaces the editor content with the
completed text through the editor model, so a single undo restores the
previous text. The editor is read-only while the run is in flight and a
toast reports progress, success or the run's error message. Errors and
cancellations never touch the editor content.

Diff preview, accept and reject and a cancel control are the follow-up
from the AI-101 spec.

https://community.openproject.org/work_packages/77784
The text transform port now takes a gateway factory, which the LLM
module registers on boot. Its gateway resolves the description assistant
through Llm::Runtime, streams the completion with the action's prompt as
instructions and the submitted content as the user message, and maps the
module's error classes onto the port's reasons. Cancellations and budget
errors raised by the consumer pass through untranslated. Without the
module the port still falls back to the null gateway, and the echo
gateway keeps overriding both for local development.

Covers AI-137 ahead of its own ticket so the editor can run against a
real model.

https://community.openproject.org/work_packages/77784
https://community.openproject.org/work_packages/79470
@chatgpt-codex-connector

chatgpt-codex-connector Bot commented Oct 2, 2026 •

Copy link
Copy Markdown

Codex Review Summary

This comment shows the latest Codex review activity on this pull request.

Review Status Commit Review trigger
📝 Code Review ✅ Completed 2026-10-02T10:14:37.636319Z 9aa3a77 PR opened
ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review" or "@codex security review".

Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 9aa3a77ec9

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread frontend/src/app/core/ai-actions/ai-actions.service.ts
Comment thread app/services/ai/text_transforms/gateway.rb Outdated
Comment thread frontend/src/app/core/ai-actions/editor-content.ts Outdated
The dropdown stays clickable while the editor is read-only, so a second
selection used to start another run on the same content and both
results raced for the same lock. The service now remembers the editors
with an active run and ignores further selections until that run ends.

Follow-up from the Codex review of #25770.

https://community.openproject.org/work_packages/77784
The gateway factory is a block so that reloading in development resolves
the module's current class, and the editor content is replaced through
the model because setData would drop the undo history. Both reasons now
live here instead of in comments.

Follow-up from the Codex review of #25770.

https://community.openproject.org/work_packages/77784
@tangopium tangopium self-assigned this Oct 2, 2026
@github-actions

github-actions Bot commented Oct 2, 2026 •

Copy link
Copy Markdown

Deploying openproject with ⚡ PullPreview

Field Value
Latest commit abcccc1
Job deploy
Status ✅ Deploy successful
Preview URL https://pr-25770-ai-101-ai-actio-ip-167-233-236-111.my.opf.run:443

View logs

The preview environment already enables the feature flags for the text
transform actions and the LLM connection. With the two admin toggles on
as well, a reviewer only has to connect an LLM server in the preview's
administration to see the Assistant dropdown.

https://community.openproject.org/work_packages/77784
Settings supplied through the environment are locked in the
administration, so forcing the assistant and the LLM features on would
have made it impossible to review the switched-off state on a preview.
The preview keeps only the feature flags; reviewers switch the two
toggles on themselves, as on a fresh installation.

This reverts commit 8b338ca.

https://community.openproject.org/work_packages/77784
@github-actions

github-actions Bot commented Oct 2, 2026

Copy link
Copy Markdown

Warning

Flaky specs

  • rspec ./modules/overviews/spec/features/managing_dashboard_page_spec.rb[1:1:1]
🤖 Ask Copilot to investigate

Copy the prompt below into a new comment on this PR to delegate the investigation to GitHub Copilot. It will look into the flakiness and open a separate pull request with you as reviewer.

@copilot The following spec(s) are flaky in CI (first seen on PR #25770, linked for reference only):

- `rspec ./modules/overviews/spec/features/managing_dashboard_page_spec.rb[1:1:1]`

Treat this as a standalone task, unrelated to PR #25770. Create a new branch from origin/dev and open a new pull request targeting dev — do not stack it on PR #25770 or reuse that branch.

Follow the playbook in docs/development/testing/handling-flaky-tests/README.md to find the root cause and fix the underlying race — do not skip, delete, or weaken the spec to make it pass; disabling is a last resort per the playbook, and only with a bug ticket. Verify the fix by running the spec(s) repeatedly (e.g. `script/bulk_run_rspec --run-count 10`).

If you cannot reproduce the flake or are not confident in a fix after reasonable investigation, do not fabricate a change or skip the spec to force CI green. Instead, leave the pull request in draft and document what you tried, the suspected cause, and any leads in its description, then assign @tangopium to take over.

Once the fix is verified, title the PR after the spec(s) it fixes, and use the PR description to explain the root cause, how the change resolves it, and the before/after results. Label the PR `flaky-spec`, assign @tangopium, and request a review from @tangopium.
On every commit, set @tangopium as the sole co-author with a `Co-authored-by:` trailer (use their GitHub no-reply email so it links to their account), so it is traceable who dispatched the fix.

@github-actions

github-actions Bot commented Oct 8, 2026

Copy link
Copy Markdown

Warning

Flaky specs

  • rspec ./spec/features/notifications/navigation_spec.rb[1:1:1]
🤖 Ask Copilot to investigate

Copy the prompt below into a new comment on this PR to delegate the investigation to GitHub Copilot. It will look into the flakiness and open a separate pull request with you as reviewer.

@copilot The following spec(s) are flaky in CI (first seen on PR #25770, linked for reference only):

- `rspec ./spec/features/notifications/navigation_spec.rb[1:1:1]`

Treat this as a standalone task, unrelated to PR #25770. Create a new branch from origin/dev and open a new pull request targeting dev — do not stack it on PR #25770 or reuse that branch.

Follow the playbook in docs/development/testing/handling-flaky-tests/README.md to find the root cause and fix the underlying race — do not skip, delete, or weaken the spec to make it pass; disabling is a last resort per the playbook, and only with a bug ticket. Verify the fix by running the spec(s) repeatedly (e.g. `script/bulk_run_rspec --run-count 10`).

If you cannot reproduce the flake or are not confident in a fix after reasonable investigation, do not fabricate a change or skip the spec to force CI green. Instead, leave the pull request in draft and document what you tried, the suspected cause, and any leads in its description, then assign @tangopium to take over.

Once the fix is verified, title the PR after the spec(s) it fixes, and use the PR description to explain the root cause, how the change resolves it, and the before/after results. Label the PR `flaky-spec`, assign @tangopium, and request a review from @tangopium.
On every commit, set @tangopium as the sole co-author with a `Co-authored-by:` trailer (use their GitHub no-reply email so it links to their account), so it is traceable who dispatched the fix.

HDinger and others added 10 commits October 12, 2026 11:49
Updates the CKEditor bundle from commonmark-ckeditor-build to 49.0.0.

CKEditor 49 removes the Watchdog. Creates the editor directly and
observes errors with onEditorError(). After an error, the editor
becomes read-only and keeps the content the user entered, and the
message asks the user to copy any text and reload the page.

Passes the initial data as config.root.initialData, because v49
deprecates the top-level config.initialData.

The bundle loads the legacy theme preset, so the editor keeps its look.
Reads --ck-radius-base, because v49 no longer declares
--ck-border-radius.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Updates the CKEditor stylesheet, which no longer contains the legacy
theme preset.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Fixes the no-explicit-any lint error on the changed line.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The read-only lock only covered CKEditor. The source mode uses a
separate CodeMirror instance, which stayed editable. Makes CodeMirror
read-only after an error, also when the source mode opens later.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Editor#destroy() returns a promise, so the try/catch did not catch a
rejected teardown. Types destroy() as returning a promise and logs a
rejection.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
oliverguenther and others added 10 commits October 13, 2026 07:29
…ata-in-wp-auto-completer

[DREAM-811] Harmonise user data in WP auto completer
…butehelptext-page-to-primer

[OP-20413] Migrate the AttributeHelpText page to Primer
…-package-name-for-column-titles-on-boards

[OP-17691] Display full work package name for column titles on boards
…ctions-dropdown

Brings in dev up to 2026-10-13, including CKEditor 49 and its new ESM
bundle. The vendor bundle is taken from the base here and rebuilt with
the Assistant plugin in the following commit.
Bundle built from commonmark-ckeditor-build master (CKEditor 49, esbuild)
plus the Assistant plugin branch. Only ckeditor.js and its map change:
the stylesheet keeps the refreshed theme that 43bf22d trimmed by
hand, which the build repository's style script does not emit yet, and
the translations stay as committed.

The dropdown now sits on the toolbar element outside the grouped items,
so it stays visible at every editor width, and uses the single sparkle
icon of the Artificial Intelligence administration section.

https://community.openproject.org/work_packages/77784
…ons-dropdown

Brings in the CKEditor 49 integration of the core (no Watchdog, ESM
bundle loader) that the base stack does not carry yet. The vendor bundle
is taken from dev here and rebuilt with the Assistant plugin in the
following commit.
Bundle built from commonmark-ckeditor-build master (CKEditor 49, esbuild)
plus the Assistant plugin branch, on top of the core's CKEditor 49
integration from dev. Only ckeditor.js and its map change: the
stylesheet keeps the refreshed theme that 43bf22d trimmed by hand,
which the build repository's style script does not emit yet, and the
translations stay as committed.

https://community.openproject.org/work_packages/77784
@github-actions

Copy link
Copy Markdown

Warning

Flaky specs

  • rspec ./modules/wikis/spec/features/admin/internal_provider_spec.rb[1:1]
🤖 Ask Copilot to investigate

Copy the prompt below into a new comment on this PR to delegate the investigation to GitHub Copilot. It will look into the flakiness and open a separate pull request with you as reviewer.

@copilot The following spec(s) are flaky in CI (first seen on PR #25770, linked for reference only):

- `rspec ./modules/wikis/spec/features/admin/internal_provider_spec.rb[1:1]`

Treat this as a standalone task, unrelated to PR #25770. Create a new branch from origin/dev and open a new pull request targeting dev — do not stack it on PR #25770 or reuse that branch.

Follow the playbook in docs/development/testing/handling-flaky-tests/README.md to find the root cause and fix the underlying race — do not skip, delete, or weaken the spec to make it pass; disabling is a last resort per the playbook, and only with a bug ticket. Verify the fix by running the spec(s) repeatedly (e.g. `script/bulk_run_rspec --run-count 10`).

If you cannot reproduce the flake or are not confident in a fix after reasonable investigation, do not fabricate a change or skip the spec to force CI green. Instead, leave the pull request in draft and document what you tried, the suspected cause, and any leads in its description, then assign @tangopium to take over.

Once the fix is verified, title the PR after the spec(s) it fixes, and use the PR description to explain the root cause, how the change resolves it, and the before/after results. Label the PR `flaky-spec`, assign @tangopium, and request a review from @tangopium.
On every commit, set @tangopium as the sole co-author with a `Co-authored-by:` trailer (use their GitHub no-reply email so it links to their account), so it is traceable who dispatched the fix.

The menu at the right end of the toolbar was clipped by the work package
pane. Bundle rebuilt from commonmark-ckeditor-build 773478c, which opens
the panel towards the toolbar, mirrored for right-to-left languages.

https://community.openproject.org/work_packages/77784
@github-actions

Copy link
Copy Markdown

Warning

Flaky specs

  • rspec ./modules/bim/spec/features/bim_filter_spec.rb[1:1:1]
🤖 Ask Copilot to investigate

Copy the prompt below into a new comment on this PR to delegate the investigation to GitHub Copilot. It will look into the flakiness and open a separate pull request with you as reviewer.

@copilot The following spec(s) are flaky in CI (first seen on PR #25770, linked for reference only):

- `rspec ./modules/bim/spec/features/bim_filter_spec.rb[1:1:1]`

Treat this as a standalone task, unrelated to PR #25770. Create a new branch from origin/dev and open a new pull request targeting dev — do not stack it on PR #25770 or reuse that branch.

Follow the playbook in docs/development/testing/handling-flaky-tests/README.md to find the root cause and fix the underlying race — do not skip, delete, or weaken the spec to make it pass; disabling is a last resort per the playbook, and only with a bug ticket. Verify the fix by running the spec(s) repeatedly (e.g. `script/bulk_run_rspec --run-count 10`).

If you cannot reproduce the flake or are not confident in a fix after reasonable investigation, do not fabricate a change or skip the spec to force CI green. Instead, leave the pull request in draft and document what you tried, the suspected cause, and any leads in its description, then assign @tangopium to take over.

Once the fix is verified, title the PR after the spec(s) it fixes, and use the PR description to explain the root cause, how the change resolves it, and the before/after results. Label the PR `flaky-spec`, assign @tangopium, and request a review from @tangopium.
On every commit, set @tangopium as the sole co-author with a `Co-authored-by:` trailer (use their GitHub no-reply email so it links to their account), so it is traceable who dispatched the fix.

The CKEditor split button asks the service which action to show.
`defaultAction` returns the action that was run last in this browser,
remembered in local storage at selection time, and falls back to the
first offered action when none was run yet or the remembered one is not
offered for this work package. The label is capped at 12em so an
admin-defined name cannot push the button out of the toolbar.

Rebuilt bundle from opf/commonmark-ckeditor-build 3c22c9f.

https://community.openproject.org/wp/AI-101

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Development

Successfully merging this pull request may close these issues.

4 participants